#include <algorithm>
#include <cstring>

#include "application.h"
#include "test.h"

#include "random.h"

namespace {

bool checkStatus(const CloudEvent& ev, CloudEvent::Status status) {
    if (ev.status() != status) {
        return false;
    }
    if (ev.isNew() != (status == CloudEvent::NEW)) {
        return false;
    }
    if (ev.isSending() != (status == CloudEvent::SENDING)) {
        return false;
    }
    if (ev.isSent() != (status == CloudEvent::SENT)) {
        return false;
    }
    if (ev.isValid() == (status == CloudEvent::INVALID)) {
        return false;
    }
    if (ev.isOk() == (status == CloudEvent::FAILED || status == CloudEvent::INVALID)) {
        return false;
    }
    return true;
}

bool checkFailed(const CloudEvent& ev, int error) {
    if (!checkStatus(ev, CloudEvent::FAILED)) {
        return false;
    }
    if (ev.error() != error) {
        return false;
    }
    return true;
}

bool waitStatus(const CloudEvent& ev, CloudEvent::Status status, unsigned timeout) {
    auto t1 = millis();
    for (;;) {
        if (ev.status() == status) {
            if (!checkStatus(ev, status)) { // Check the shorthand methods too
                return false;
            }
            return true;
        }
        if (!ev.isOk() || millis() - t1 >= timeout) {
            return false;
        }
        Particle.process();
    }
}

bool checkData(CloudEvent& ev, const Buffer& data) {
    size_t size = ev.size();
    if (size != data.size()) {
        return false;
    }
    size_t oldPos = ev.pos();
    if (ev.seek(0) != 0) {
        return false;
    }
    char buf[128];
    size_t offs = 0;
    while (offs < size) {
        size_t n = std::min(size - offs, sizeof(buf));
        if (ev.read(buf, n) != (int)n) {
            return false;
        }
        if (std::memcmp(buf, data.data() + offs, n) != 0) {
            return false;
        }
        offs += n;
    }
    if (ev.seek(oldPos) != (int)oldPos) {
        return false;
    }
    return true;
}

bool writeRandomData(CloudEvent& ev, size_t size) {
    char buf[128];
    while (size > 0) {
        size_t n = std::min(sizeof(buf), size);
        Random::genSecure(buf, n);
        int r = ev.write(buf, n);
        if (r != (int)n) {
            return false;
        }
        size -= n;
    }
    return true;
}

bool genRandomData(Buffer& buf, size_t size) {
    if (!buf.resize(size)) {
        return false;
    }
    Random::genSecure(buf.data(), size);
    return true;
}

inline unsigned eventSizeInBlocks(size_t dataSize, size_t blockSize) {
    return std::max<size_t>((dataSize + blockSize - 1) / blockSize, 1);
}

} // namespace

// TODO: Many of these tests are actually unit tests

test(01_initial_event_state) {
    CloudEvent ev;
    assertEqual(std::strcmp(ev.name(), ""), 0);
    assertTrue(ev.contentType() == ContentType::TEXT);
    assertTrue(ev.data() == Buffer());
    assertTrue(ev.dataString() == String());
    assertTrue(ev.dataStructured() == EventData());
    assertEqual(ev.size(), 0);
    assertTrue(ev.isEmpty());
    assertEqual(ev.pos(), 0);
    assertTrue(checkStatus(ev, CloudEvent::NEW));
    assertEqual(ev.error(), 0);
}

test(02_set_get_event_properties) {
    // Name
    {
        CloudEvent ev;
        ev.name("valid_name");
        assertEqual(std::strcmp(ev.name(), "valid_name"), 0);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    {
        CloudEvent ev;
        ev.name("very_looooooooooooooooooooooooooooooooooooooooooooooooooong_name"); // 64 characters
        assertEqual(std::strcmp(ev.name(), "very_looooooooooooooooooooooooooooooooooooooooooooooooooong_name"), 0);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    {
        CloudEvent ev;
        ev.name("");
        assertEqual(std::strcmp(ev.name(), ""), 0);
        assertTrue(checkFailed(ev, Error::INVALID_ARGUMENT));
    }
    {
        CloudEvent ev;
        ev.name("too_looooooooooooooooooooooooooooooooooooooooooooooooooooong_name"); // 65 characters
        assertEqual(std::strcmp(ev.name(), ""), 0);
        assertTrue(checkFailed(ev, Error::INVALID_ARGUMENT));
    }
    // Content type
    {
        CloudEvent ev;
        ev.contentType(ContentType::BINARY);
        assertTrue(ev.contentType() == ContentType::BINARY);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    // Data
    {
        // Buffer
        CloudEvent ev;
        ev.data(Buffer::fromHex("0123456789abcdef"));
        assertTrue(ev.data() == Buffer::fromHex("0123456789abcdef"));
        assertEqual(ev.size(), 8);
        assertEqual(ev.pos(), 8);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    {
        // String
        CloudEvent ev;
        ev.data(String("abc"));
        assertTrue(ev.dataString() == "abc");
        assertTrue(ev.data() == Buffer("abc", 3));
        assertEqual(ev.size(), 3);
        assertEqual(ev.pos(), 3);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    {
        // EventData (Variant)
        CloudEvent ev;
        ev.data(VariantMap{ { "a", 1 }, { "b", 2 }, { "c", 3 } });
        assertTrue((ev.dataStructured() == VariantMap{ { "a", 1 }, { "b", 2 }, { "c", 3 } }));
        assertTrue(ev.data() == Buffer::fromHex("a3616101616202616303"));
        assertEqual(ev.size(), 10);
        assertEqual(ev.pos(), 10);
        assertTrue(ev.contentType() == ContentType::STRUCTURED);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
}

test(03_read_write_event_data_stream) {
    // Multiple writes
    {
        CloudEvent ev;
        assertTrue(ev.data() == Buffer());
        assertEqual(ev.write((const char*)nullptr, 0), 0);
        assertTrue(ev.data() == Buffer());
        assertEqual(ev.write("\x01", 1), 1);
        assertTrue(ev.data() == Buffer::fromHex("01"));
        assertEqual(ev.write("\x23\x45\x67", 3), 3);
        assertTrue(ev.data() == Buffer::fromHex("01234567"));
        assertEqual(ev.write("\x89\xab\xcd\xef", 4), 4);
        assertTrue(ev.data() == Buffer::fromHex("0123456789abcdef"));
        assertEqual(ev.pos(), 8);
        assertEqual(ev.size(), 8);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    // Maximum data size in one go
    {
        CloudEvent ev;
        size_t size = CloudEvent::MAX_SIZE;
        assertEqual(size, 16384);
        Buffer buf;
        assertTrue(genRandomData(buf, size));
        assertEqual(ev.write(buf.data(), buf.size()), size);
        assertEqual(ev.size(), size);
        assertEqual(ev.pos(), size);
        assertTrue(checkData(ev, buf));
        assertTrue(checkStatus(ev, CloudEvent::NEW));
        // Try writing one more byte
        assertEqual(ev.write(buf.data(), 1), (int)Error::COAP_TOO_LARGE_PAYLOAD);
        assertTrue(checkFailed(ev, Error::COAP_TOO_LARGE_PAYLOAD));
    }
    // Maximum data size in multiple writes
    {
        CloudEvent ev;
        size_t size = CloudEvent::MAX_SIZE;
        assertEqual(size, 16384);
        Buffer buf;
        assertTrue(genRandomData(buf, size));
        size_t chunkSize = 100;
        size_t offs = 0;
        while (offs < size) {
            size_t n = std::min(size - offs, chunkSize);
            assertEqual(ev.write(buf.data() + offs, n), n);
            offs += n;
        }
        assertEqual(ev.size(), size);
        assertEqual(ev.pos(), size);
        assertTrue(checkData(ev, buf));
        assertTrue(checkStatus(ev, CloudEvent::NEW));
        // Try writing one more byte
        assertEqual(ev.write(buf.data(), 1), (int)Error::COAP_TOO_LARGE_PAYLOAD);
        assertTrue(checkFailed(ev, Error::COAP_TOO_LARGE_PAYLOAD));
    }
    // Random access to event data
    {
        CloudEvent ev;
        assertEqual(ev.write("\x11\x22\x33\x44\x55\x66\x77\x88", 8), 8);
        assertEqual(ev.size(), 8);
        assertEqual(ev.pos(), 8);
        assertTrue(ev.data() == Buffer::fromHex("1122334455667788"));
        // Reading from the middle of the data
        assertEqual(ev.seek(4), 4);
        assertEqual(ev.pos(), 4);
        Buffer buf;
        assertTrue(buf.resize(3));
        assertEqual(ev.read(buf.data(), 3), 3);
        assertEqual(ev.pos(), 7);
        assertTrue(buf == Buffer::fromHex("556677"));
        // Writing to the middle of the data
        assertEqual(ev.seek(1), 1);
        assertEqual(ev.pos(), 1);
        assertEqual(ev.write("\xaa\xbb\xcc", 3), 3);
        assertEqual(ev.pos(), 4);
        assertTrue(ev.data() == Buffer::fromHex("11aabbcc55667788"));
        assertEqual(ev.pos(), 4); // data() doesn't modify the current position
        // Reading from the beginning of the data
        assertEqual(ev.seek(0), 0);
        assertEqual(ev.pos(), 0);
        assertTrue(buf.resize(1));
        assertEqual(ev.read(buf.data(), 1), 1);
        assertEqual(ev.pos(), 1);
        assertTrue(buf == Buffer::fromHex("11"));
        // Writing to the beginning of the data
        assertEqual(ev.seek(0), 0);
        assertEqual(ev.pos(), 0);
        assertEqual(ev.write("\xdd", 1), 1);
        assertEqual(ev.pos(), 1);
        assertTrue(ev.data() == Buffer::fromHex("ddaabbcc55667788"));
        // Reading from the end of the data
        assertEqual(ev.seek(7), 7);
        assertEqual(ev.pos(), 7);
        assertTrue(buf.resize(1));
        assertEqual(ev.read(buf.data(), 1), 1);
        assertEqual(ev.pos(), 8);
        assertTrue(buf == Buffer::fromHex("88"));
        // Writing to the end of the data
        assertEqual(ev.seek(7), 7);
        assertEqual(ev.pos(), 7);
        assertEqual(ev.write("\xee", 1), 1);
        assertEqual(ev.pos(), 8);
        assertTrue(ev.data() == Buffer::fromHex("ddaabbcc556677ee"));
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    // Reading and writing past the end of the data
    {
        CloudEvent ev;
        assertEqual(ev.write("\x11\x22\x33"), 3);
        assertTrue(ev.data() == Buffer::fromHex("112233"));
        // Reading past the end of the data
        Buffer buf;
        assertTrue(buf.resize(3));
        assertEqual(ev.seek(1), 1);
        assertEqual(ev.read(buf.data(), 3), 2);
        assertEqual(ev.pos(), 3);
        assertTrue(buf.slice(0, 2) == Buffer::fromHex("2233"));
        assertEqual(ev.read(buf.data(), 1), (int)Error::END_OF_STREAM);
        assertEqual(ev.pos(), 3);
        assertTrue(checkStatus(ev, CloudEvent::NEW));
        // Writing past the end of the data
        assertEqual(ev.seek(1), 1);
        assertEqual(ev.write("\xaa\xbb\xcc", 3), 3);
        assertEqual(ev.size(), 4);
        assertEqual(ev.pos(), 4);
        assertTrue(ev.data() == Buffer::fromHex("11aabbcc"));
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
}

test(04_resize_event_data) {
    // Truncating the data
    {
        // RAM only
        CloudEvent ev;
        Buffer buf;
        assertTrue(genRandomData(buf, 100));
        assertEqual(ev.write(buf.data(), 100), 100);
        assertEqual(ev.size(), 100);
        assertEqual(ev.pos(), 100);
        assertEqual(ev.setSize(50), 0);
        assertEqual(ev.size(), 50);
        assertEqual(ev.pos(), 50);
        assertTrue(checkData(ev, buf.slice(0, 50)));
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    {
        // RAM + file
        CloudEvent ev;
        size_t ramSize = ev.maxDataInRam();
        assertEqual(ramSize, 1024);
        size_t fullSize = ramSize * 3;
        Buffer buf;
        assertTrue(genRandomData(buf, fullSize));
        assertEqual(ev.write(buf.data(), fullSize), fullSize);
        assertEqual(ev.size(), fullSize);
        assertEqual(ev.pos(), fullSize);
        size_t newSize = fullSize / 2;
        assertMore(newSize, ramSize);
        assertEqual(ev.setSize(newSize), 0);
        assertEqual(ev.size(), newSize);
        assertEqual(ev.pos(), newSize);
        assertTrue(checkData(ev, buf.slice(0, newSize)));
        assertTrue(checkStatus(ev, CloudEvent::NEW));
    }
    // Extending the data
    {
        // RAM only
        CloudEvent ev;
        assertEqual(ev.setSize(100), 0);
        assertEqual(ev.size(), 100);
        assertEqual(ev.pos(), 0);
        Buffer zeroBuf;
        assertTrue(zeroBuf.resize(100));
        assertTrue(checkData(ev, zeroBuf));
        assertEqual(ev.seek(100), 100);
        Buffer randBuf;
        assertTrue(genRandomData(randBuf, 20));
        assertEqual(ev.write(randBuf.data(), randBuf.size()), randBuf.size());
        assertEqual(ev.size(), 120);
        assertEqual(ev.setSize(200), 0);
        assertTrue(checkData(ev, zeroBuf.concat(randBuf).concat(zeroBuf.slice(0, 80))));
    }
    {
        // RAM + file
        CloudEvent ev;
        size_t ramSize = ev.maxDataInRam();
        assertEqual(ramSize, 1024);
        assertEqual(ev.setSize(ramSize + 100), 0);
        assertEqual(ev.size(), ramSize + 100);
        assertEqual(ev.pos(), 0);
        Buffer zeroBuf;
        assertTrue(zeroBuf.resize(ramSize + 100));
        assertTrue(checkData(ev, zeroBuf));
        assertEqual(ev.seek(ramSize + 100), ramSize + 100);
        Buffer randBuf;
        assertTrue(genRandomData(randBuf, 20));
        assertEqual(ev.write(randBuf.data(), randBuf.size()), randBuf.size());
        assertEqual(ev.size(), ramSize + 120);
        assertEqual(ev.setSize(ramSize + 200), 0);
        assertTrue(checkData(ev, zeroBuf.concat(randBuf).concat(zeroBuf.slice(0, 80))));
    }
}

test(05_save_load_event_data) {
    Buffer buf;
    assertTrue(genRandomData(buf, 3000));
    CloudEvent ev;
    ev.data(buf);
    assertTrue(checkStatus(ev, CloudEvent::NEW));
    assertEqual(ev.saveData("/tmp/test_data"), 0);
    CloudEvent ev2;
    ev2.loadData("/tmp/test_data");
    assertTrue(checkStatus(ev2, CloudEvent::NEW));
    assertTrue(ev2.data() == buf);
}

namespace {

Vector<CloudEvent> events1;
Vector<CloudEvent> events2;
Vector<CloudEvent> structEvents;
Vector<std::pair<String, String>> oldEvents;

void eventHandler1(CloudEvent ev) {
    events1.append(std::move(ev));
}

void eventHandler2(CloudEvent ev) {
    events2.append(std::move(ev));
}

void structEventHandler(CloudEvent ev) {
    structEvents.append(std::move(ev));
}

void oldEventHandler(const char* name, const char* data) {
    oldEvents.append(std::make_pair<String, String>(name, data));
}

inline String scopedEventName(const char* name) {
    return System.deviceID() + '/' + name;
}

} // namespace

test(06_connect_and_subscribe) {
    // Prefix the names of events sent towards the device with the device ID. This way, the tests
    // can run simultaneously on multiple devices under the same user account
    Particle.subscribe(scopedEventName("abc1"), eventHandler1);
    Particle.subscribe(scopedEventName("abc2"), eventHandler2);
    Particle.subscribe(scopedEventName("abc3"), structEventHandler, SubscribeOptions().structured(true));
    Particle.subscribe(scopedEventName("abc"), oldEventHandler);
    Particle.connect();
    assertTrue(waitFor(Particle.connected, HAL_PLATFORM_MAX_CLOUD_CONNECT_TIME));
}

test(07_publish_text_event_with_polling_and_callback) {
    CloudEvent ev;
    ev.name("abc");
    ev.data("n7nuWm8oPlTegXFANsoCdnrI1Jz4oGQ3Vs3MuFG6BIvl3TXVl3PFst8BeRkZPfam4kB9RblSFH7Rwx6C1DmsK1Ctnp1DovdzkvSR"); // 100 bytes
    Vector<CloudEvent::Status> callbackStatuses;
    callbackStatuses.append(ev.status());
    ev.onStatusChange([&](auto ev) {
        callbackStatuses.append(ev.status());
    });
    Particle.publish(ev);
    assertTrue(checkStatus(ev, CloudEvent::SENDING));
    assertTrue(waitStatus(ev, CloudEvent::SENT, 30000));
    assertTrue((callbackStatuses == Vector{ CloudEvent::NEW, CloudEvent::SENDING, CloudEvent::SENT }));
}

test(08_publish_binary_event_of_max_size) {
    CloudEvent ev;
    ev.name("abc");
    ev.data(Buffer::fromHex("ff8482032e330a53601242d279ee0eda3b0e9bee571e05dee7580d0962c9fda9c8a0e27f3e15de85fb63d8c2824b891760c7728e68fbcc4331ee3cd9b8da56d6a3ea80a22e78032a20b9bcd6b844281f4d0ab784fde4c9f15d92dc747692a3940238d3303dbc56e413afadc8add8383777c7c0abc6e57e3ab58a77c7f8fef5630243236fef061030e8ac82399cea185dba9f674ade367a0ba06b3257699866145f1a4662caefcc340a348ce68d06991d67cb1af93ded3cae622ea15f047d35f85f2ad42f22c706b002d69d1b033803ba7f4d46be06eaf64671f10c9c6f6372dfa84492381ce40ef65ee69d2a0ab3e33f178bea883fb8b8c3e7ee4e02feceeb1a99de001152a49d38a01b26b06c2d027d8361b6d1e0dd00867ee801873b0fc061c13384701f78124a02825c6fe2dfcd2f76305726d408e1765fb0299fc00255b6e2d28000d81105db912894eea5d3c73b71129938721e53002633006047d7fa8e3ea4a1ca7773a597e60efd265203e39411f523d2716e5ff02b32e90e5aae7194f8c5c5e62d5a08c416a0ca18c95588b4d431ef5309260e613bae3d32732a432f0035bdde3c8510236c62cd968efd656d3b5e59efb47ba946d8ac51ff81c5908953088052308296db0bce61d66a87dfa6541a13382fa2b55ba666df845d4458bf9b9a8abddc35263a2b11c55b6e634972acd99c38a5885a80230b2769fe3c3aa2f41845e71b95ff76f002e99490fe574f6b0a1aa322c7083651dcedf9168d272ad6e49d24eed7c2171918e381da1dde565673d188bde2cb07f1c96955c82f0f3ff1d020cdcd7599b98b7eadc76c551768b3591af5155d564e95afde4043461ab6a645b19890c12b562e05a1fdef95683f272f861bdef68a2a4ac27e2eb76556ef1662bd97b478e396e0c7dd4ca4cc53cf376b56151e12a97a5a2cb7401ada20eeb3a364fc013c78c7dd08dbfbf8573c25f6b4f55de4baef9b07e8a0625ebd5aa2e0ebf64522a16e56ea80fe6be686c27ac9ede4deea8d8cf33d9627bcfdec2754833ded30e6c2fbc6ac0240bb81e406928bd20e977aff7a2be4cd25ba6aba2fdc729e0d9031da6a26fe7f7c3f29246a9618c715dda5067b5b9141f0d5d3af3602dfc29231db23a108257e59daed5e4f3c07cae19caef9da5c33cf45c8a22656eafab6fa65e9446cb543ab92a6b2eca81c585757f2b8a2f10afd70ea000ddb0f73c3327fd4b77d8ddb887d729c1b208aa923e90e5127e1ab97ea04e0885d58c1d0964b72fe1ca705bd86f333efd771ae0db2a6736d3d0d3d38ea3cbdbe1f84dda39f545ab4894f109fc3208dccf2c52711a469d4c9bf32aa239e86a84eaa855e22fbca957b2810c4cfa2aba8f773ebf90d0741d59bcef2489a379f7e731931e84d5f426d4f80abac16c02537762a3a0b39958f8bc9652ceb10c5698f00c95f374665e6a6de7ae7c8c039543d5e728ffe8089466ad36224382f9991230d3af3cacbfd100dac48076218cec35ace951c4bf919eed2b5c0609b120e6e86c5b836bc12335cb404759c77091935f62ae1ae57806d0024ca0f9c75dd68778ff2a4e4084fcb668f17f444b91cc81859bb69467e0a4c2084a49b7908350a387ebc9f388f541b659b1e0f10aa326bfac7ed660706a8fb9e34c1c4ba4cf1105e824c88b3faa7faedd5488651d1cacd9b5147076195bf9711f34175bc4db084bc7a09990ce971005506c6f283b54f145254dea94bad2e383a33aa7de2d7d19ac5dd90b97761ccf3c3de6480c442202effa410a517dc4696365d0d16fc5a8f1733c5afaa9054b795b810b290858998192242023fc432ab9eec82ccf1a1b924b59726236fd3af2fa2a2e09b60e65926e87f5115754384fc7763f6626bf3673882cfc7da1769bb98fb353bdaef2782db1a355281d57fc3ac5f5c99427e46f1f2cee35ee5a03fe7d32a031a2a911fc500e51c71d471f7be9b6dee82c04fbe0273bc1ee4922e66e74d74e18197fb88a297a7604d066d67bf7f518e05d0cff607ec6f3d79fbeec7cba9e978974db0c5c9818f4cc0e661c815192f5e3088d7d80f7aa8ca8a01116deac89ecce17b2c346507190faeebe81965a1ee92d0c1f530c0f3fa5e9bea69c974927d3eb280435b2e5c2d580020c055dac01f13b46659142c44e3497f540061a4c3059e4cd6dca93cc248cff0c79d7759eff42000f591d46ea6ef4dc735a059746e22ab255848fd63717268642e97b64d322fb56caf728ecbe2c868c48fe2ca04156109b2813000602885c38b1ce10b3bb762c5029460522ccd023746a73f70730c707d7135138b46dc1e271730ab445e369cf72f1313d59a1707476e781f0dce766c61e6932544747af64722133d0a51233ddf2f2c84e3bedea5d7b08e6f9c4cbe16fafec35abd887dc9abefa83b4326a6d5c1c48fdbafd48547b8d6c51341d7cbdab7542c30d9ef98bb4d88ccdb34a2d8c547420a5f836e06a61f1b3e9a584e7c5d79670273ccb0a0d3f551e63249b1b9830997e873f984d5205e8b6cc1c3173cdd1f84c00e34e085f4b3674f55cbddcd5c9f4764311149ac6af7b3d4ceda118d4a25d0a1b05bfe31d4eae617efd88d6a57678bf3b71717f24dc1e11a70d88483fd20e0dec602be89f28e961e5a5dc3102d5e5598a21ff3a65207ba7c0cc4e6e85076c081456128a027c4a41389f966b85dd4a139a0cb41752eba453ff4a009f2fea55b7c48aa9054343d0d91c24903ef78c9e29aa4727702f626d0a5fd114fd173d243066df008ca0803ac390b3739b16b435761511002209eea1da8c3bb9a048479e1e561fe3e474eae65699df251b813863922d6cd3698666fa3099257458cdc26709d8dabd5648578c99261ad480ed8c277cc732fc8fb16afde34207f8d58de446c9b4aa2783d030a90fbb9cc20948b26a6b70b9378b0039a69458af2b05214b588703eea9a8986d40166ad6080a284bb3d31d6729781ee930dd7194431e0c892cee615288f6c4898d46565c94e5f6d05746c7ba0d765c023b37a352a85e36a5f3a208c4a9ac0883ce0c9f2afd4c811d9c7a330ba97a5c1329f81838a446f3caca946ac2113481154de3092e3322be44c866b70a02d8b4de1ee472333b5b815d9ec593d0ecb70cd81818bdf21638dddd10214becaeb10980db7c43334cc08fa3a00fca728ae1bc6c2471c043e17363731a2ec9a086514f4c916cde0e4a67239bfd88ba3e5a12edb6ad0a321045bcd921ec781c1cf806b3a4988b5c0a2226fc5311bd844556bfb23327721c4d5ccdbe777207c9787ab235ac6dec0f9c838b0c1d070a2d9dc55ef891b4dd23f2e4d9cd686d5063d7417c6c4e08da053a8ee1e2741b176e8dce061c6b56ba6683797537d79abdfa9d44bd35375970eda69dced00f70f44e5e32e25f6bb9ba1ef34023ce89a5ffa94d69d3e99f4af52f591f003f60450575c857db59ae39d6761f3cff0867dae27f5b9dae3fac1408f6002e98b4adecefd7d46fe7b50d6ac667ecc757f1001d7d98a0a986c57a660c2a1c2cec4d77bb86fcb5e5a8ea9dfaf9ff04df3d335db4a93b4489a6874adaa80da4aa04e0f3b15e28aaacad6814d045fb65f45d84015c7c110d5473aa48aaafdf3e888d4c3a838f283c72af0b7f187597300fbd805bdbba4c921bae70c800a16572d7755caade70e5c463719125f366c3ded600ab082a73d5682bb1a632598881b4bd352ecd57e28855db5cbf60158e13324a864aad8052e047021232d5e58e93e209c7fb2840ed3969fa9e86d04be65fb0426c7a7b7184abde5ae3c317bdbe646b85afb9383cb90f14dc39954c3247ccad383b6e318dc440d184949d4aa7f3a758084fb34c25a1d811a345208f8fddce46c1391fb5d50714674d278c73c9292097316dcfa8acd940909b2949c2a9d839c029beb8db115b82bb9f621580ca80a2960226f55b013294359c6a366e054865c3595e023cab0ef3a357f850162d4e634e371c2e32978b2a36d7422f69ff59a362f842c99de53251d2a40415a47fc489b12111d384af3f7e00106da068a7c00c5685c7f3cef50a1f823e2f6b6b1155ddf23039dbb627271729b7814c90c6ce6feeecdcf8b9b23c20864bf6f4e7fb592e31498c655ce0b5bc8df1640dc2bb1f7f534e60b448b2313bd7f7d97e6c52f2354f83088faac1019a024416e8f8ea18a8699e516f4be2d7dd5a8b65eb7674e5d4ffe73d11b0a78b19d0c7fc4053309f9b20d5f11e4b54c1cf21d9cdacf265bdfeaea80c9b96ff6cb919ae67e25c1f5e84057f4546aa8d32dbe2271551d561ec08f81a302164d54a41d05c10c8093302d11dc0b296cc9fa7d819e711065f7fcbc04d5238b98cb6505786918743ec3ca90974584b4b30158ebf1d5996fef9b085d73c052b57a1bcae58f7497120d0b9ccbf3c24f1660093fe2188c37f83512ada1fc2f5514d3c9f3879ab45012ff52fbbee12ac23e56b74c8ed425f6df665d080c67ab4fed3e8ba0b02d544f46d9905f742d6489224fa1a136018775f533f739a6ddb4d9b1914224809f4b83e4e0734e1e58edbdb7511b5a89f6032e98fce4f1f5e4807b4a0eb5f100a57ebe663479ac4feaac73a1fb1516dca733c203eca57b271d48fd58adf8c731898a7ea857106791f458a9a9e7513ba42b8547f15529d4e6c48fa80b92c833e45922b42af9b1b1f341cf26bf5e5116d748a0b6c4ca752647e9e788ed711772d0659833de11012309adf16f509068348d95a8b16f7677a7ec3b1892744bdf169602ca69d4f57389c8b6e2f10393f48ba9f5277dd68673e1a480038201f14a4afb82a8e873e7c5cee17868b3da169a0e742b263027caefdd4ecf0f88764f8bf4ec0ca210287e9241d6073e50a3bc8e58c38143de367fa5c80b73b79d91d68519228068f0e4fa93298c6a59ff08e9a92cf2273a93bf78419ab7c68d2c12c165fc033d8e1dd682ee31906a75232b7237db9a2d780f8ec7e9290f61cb2aa1f9ff26b8d45a31f1d118c8bd7021f59a3c8337a1f5cda84573cf366d771c65926d752d058405b7d0c7bfe2b2ce7ba9b9c87f13dcadb04b83bdc31cc8b12676aa40c1ef9d2ad55faf968a03a52bccb5bdce2520dc7d8549d4916742292746db42da3c6bdfa36e981fccb6900956222b0d5141646922b06d40b352b364dd1d3aab2346b975147ccbed9d94fcfc69750a0f82ae804b1b4e88518976eb2a74890142f594756a0309f62ee5a65c83c1738806af9fe3a23cd45fb26c9d0272ce2b8b4c57c45f3c9157bba3bb5ff7600743a6b9564d2a49aca65c9ef0be524d93e0f0529980bf1e445c308efb78f36bfb437627224def193e5c7d14f8f52933afc7ff3b2117574f2734418815816118a27b922c734175cc25d031f8a00ed0f43c8fe1cbe15e1901e7e1638e16bd9d90e8c23f9ab8798561ef23682e306e0e7061d93c29348d0a96e2d79852157734ef59e92db14e5bb949b88c89c8fb807d98211b198e4ad5aa5bf0faf1fb950719cef87b032dc456953e4edccc33399e766b3d276c8ab528a35bfcf002a8e48a3e4fd486e0d644f3dcd97624ad7c8aa5b590cb3aed8f1284faf03db17809f4a0a8513e629ce0d575fd9e7f58136aab49ceaffb986c49080e46df3afa9276e6fcffb2cd28b7905b6454fa0d7e60154f436b43219f452a72feda5e1768009a115100e6fe56ab25c8ad0be106937311c1f73a528ff5d614a1c333a80280409f1302ca96a879ec937a3c375ea90485b64008f495aaac4512e0828a9109034cb4e556f5891fce7411ff5b7e56fd7d26e07ade4f8817a8ee5853ab63773dbc20236087f2536c6eb46ba25d5f8ba44175c45182b15c1e4d4ad4b690ec96aa187c29f73720cad4481782c6c6e3005e28ee0070a13d5e2f8db66c5db8acac6e03ea6a5a95e09609a4e39af473bbda353371043ea30df48355717a7dc47391701532606348355eca35f8b90a24fa613da619fedf30c0b55f1cf55df428aea742e6618a475ad9ec75172613c0255dc2841cbd69ff2abf317c0e295a66f4ccbc27406336f5ee9ae5e8cc716339dccf526402713d88d3673602387f80b1ad9a2e6d6244efc9585abfacb3e24dc17268c9368d45569aae0c83e60645e090b8ed99b52b4f1c598de2ca89d7443bc06551a707f2b50a8ca17e9a217377ece848bc944ce2b1fb92ad4bcccd6a509103cc4bf7edd0eace33799e0c2e3d5df7995f0ad07ffedf98784434c99298c8b27b6523da339ccb6b639a12313a770775381ec57479f3f5516a6a7305efc4d41daa984b1d87b33591da89ffd7422b47d51dcbbf6838b9aa728502e6a6ca10ebb0a0bae2bfaa2154d14678239c39fc9992ca52cf78393cd3a4b83f9381160c6b0549241fff2064880a3b909d4ced598e3056b889e3463b3bd4c1909bcb09dea1139c781fe4d7c1b5c609a1232d5c95653590bd9960fd60666b882d28cf8c17dfbb0abc25ab93f4bf59606835aceaac402d2e92c1e497ec9e13697c80b00bf157a68a60bec18db8ab966e51739fe1b9f37a65a6307a0b427792fc36604696df58a7a4eff83fd5e10e70052418378a6a7e3f456fa2703d913d55e4d1387d673a1384ff5bab9de0e884ec6fb97f38581f9198cd4b708bf8fa3f7c59c761f5d05f19f118a8bcf9467d09b09168bdf14bc939293b76d50afe963ba216c0dad869857e4f6fa044387d1e970c35ad5d0979b56c952645e44250e1586b0490b311db423aaa8692a763027741f996f910850086ec8b6945fbbd9b2021a52b79a9e09289264a5d1544c85e01d37252873ce48baf08657fc60a1199024c5741bdbfdd871a5a3bb9ac6fc89fc27b5436749318c74103436763283cfe044bf7869fb480097ba08a64691b9225746e2e9d22d7dca5018813f6a3e4deed0dc7e3ff15ee11fa01a5d5e68e09c2c9770d18f594211be2312dde174e8cb0f1a7253094613bfaddac09123bf3bc3b5bb3568fb18f0a0ae011ff39717e108626d8d14412ed9bcebc4fb20bb6fd07dd867c1b090177c9bbb436b4d0b37e1c283d9b1756cfbb4d869530ade5c2177238508c8ba330e433dd48738cfadd15a8c41e7c971a51be7a4d6c8227769a0a1f8d22fc1159db20aba7cfda25b8fec942e79c8f2bb0bcf50e5ca7276b4e12a27579cc2fb896312e1660eb223f8057cc0a7756596dcb77336703d7a601661a4cb800a9d3eb4583ae71869585c486aa46ae870b6cd160001eac46d688445426698160abe7a1cfd3a356b4f01004fb20928ac676cfbeb37d208cc7601ca06056723924e8f5fc8483269a0ae257e4e61aee4b107b561f2aff8ce6a9a9976352ade270f0ce8b1df14264c6fbd16889a55d5b0c115ef953b98080a0a89c1f616c9975524cb0a7d75d0ef4788d1e88ff7e5db15b2e6c480422738c52cda33b63190e347192519c8d7d4d3c4d6b5f2162e36c851b0615311140b094e1e751f0e4b6bf2c894408e1eed9a04d030a7befd17a04bf469ded52e3be7cc5a022cea3a546b4a6e758d7b34e624de6ece20f68734bb114f15463eb70c0bef47a74c8a1d7cb5664b83388ea0c16818fa4175c0e8ab0d722b9c5bcd5629beb575a8601bd8d54d3f71541d5dfa16ed361b68412fa33e8a5b57e48194475c16e3c63cae99be426d26db7ac4c696e89734c66fda25a27e0841833fe74bf16461e9ec7e44a14e152ae2d3635beef86b735bd7b038bec11d759a9b0f599cd36cb9cdf7a60f939a58ac162804110638f0ecbfb76309a9b6a505c798d52bbccfa1ef4a56670f531a07c96f4d9144057b7c38f2ce2ccd11fa9cf263ae58eb18f807b08dd7ed3c9aacf1f2f6f42113bccdd0fb2abf75cda2562fe63bf5b99b4e0c62ca8d57b101070e5a76235a254f1ca8c5e58a557f7c3afef7c5774f23b3127976f44c51855545dbf7f8ba47754326a10c24be8c1a9d02e9cd58e0990255df9376d6d4ff13f8f9bd5fe96379de18e6362e2960b00b364fecedaf3773e6c9e05343c7c033d64d79dfdad5600d5e42d1e56c9b3e58d4fec120098d673dcfa7e53a3ff7ba88778f9c772922ef0dca3f1bfb6a26cb5ec29dfc1546b5ac3180e41636bc51fe4759743ccad17482bc7083ca1acb8f461f4425dab49c360059267eedbed9bdf5a282b43800c0780c68b7333f93886d18ce6859855cf1e13f5b9434ab8d986cad87b0ff61e1d315bd2d6f19dbc8669f466af6133af0c8912c6638e84705b0ba2c95c6027f323d223593eeca42801997862773ec8ae195cc243d76146ce69440f4a9ea202c3ce0a2a5f27d3602846552b3b8122dfb665052b0fa0d89ccb02a504ed00cf26d6e53b44906b25f385931f9414ec0550bb1d2a2c90f208ff7bc79f5d21f899d3a6eebac4c7cd63d06fac23d3e746f9194c93c24e893599d9d84e4fb2d7916ec139326776976ce5f870bb1e46794386c22ebf98f54eefc802efd9e6021c7387ea1384660c4be26ad90d26b4715e0b84f2ba2d2dbc6fad46455218b87ab9b3fea616b9eb275889cd73522280886c4d4f7c11fe981afaec605c5a9d11deac0968bfee339297afc87d3ee41e7f67ab26069c0ef00207bb0f788764652e33693c3250f61db3e084fde9cd288950e01e4ea8488cd29c258f29441ec3d5ecf06ace4adc815eda47307bdc87d134af170861d499823a6859010e21a2d63621c63f6b0318c0a28a124c02d6d91eb04f6421c88536559eabd20c3eab5ce2c821984cf4faaf34d14a8f5d8afa90b85a7625389ba35450d781d5347e38711a2b52f58875c78d8061511d891f12ac91dce515ae4dbb28de17e3216e57f819a36a4f39a8efc5d7cc242e5028c07b205c6244ad09cd4e5d3b11d88803b300838690fcd5f7f86d01e865585e37ae64092e82cd7a968d98c82d9f85fe0c78efa6abb7ce9023a2f9c9760413e0033f03576f8350a031282ba42056b5ff73cd7d93691f03bca6e18d2c092077478608434b0b8da2b957be6612ad780abb54512be17ff8ee9a7b44fd02af63b4da511de9b715c81edca9e3b89c0e61cb46b973ad097e42de070af5b34a15aa0e9cbd77f5b2b4042b759bfbf1fe31042f22fd0852eaef526b24664c2e181d39e9d63c0975d0a345e99132a79d8d8f16877e05ea63f76de77fd4b10982473cfd26b7c27f4ae506f48457f6f14dd401a1c92866886ac2c603be9367a0f6c942fa6c22565c12187f90d45bf318228d3646690740fd9f07d9b024225d357f9e5bce77791497e14802b9f430e69e0b1b9d54f09ca0c9c3f8078dbc1c244574365e977b1f475f8204cdb2cd25470fb79abc60ad7aad4b76f2ae574262abb56998b2e76bda25f79ad9da443b22681a862f1a1ef8a9bcd546faa776010928bd4dca4b5e216c0f43a7b027816eba846012c5e772718184a60dd6ec88324f11782054d6e36f04996f2ab3f648a237bc4114b91bc858191b1ac7106ce8709f62fbd7cb430f2bdfbfcea319dc697830847e10f864a488ee10c13d47e3da7b642ca9b8ecc36c1741f8ca7b34db2588d96fac47d400a0f373b62d33fb34078e5729a5b077ace0b833f5383f7250a6c2076b3068e1e6a89cf8dbc9354f518123b2da5554fc20533dd2672c4e406610c2cea78bf9935156e75e9aed6d26cd3893f8741cde83f8f6346767cecb92ecd650c64b5579ed78974e7971e4d7196271bc8d8d69f27879ec1b29eebfde9996ac053ccc4d09d5b01b2d871f01ffe66989f26e96bd299531e6bb7c5a978efd8e0e0fa418fcca2cdcc78d032fd753c9a3c56fc14e4bff59a8fb0796bba2931df0ced6c434102409f156eadf392b8abcec4234c610c53ebd0c6a2fe3dc60da87b17889b9896e7d1e08d1b320b0d8adc89ef86f75754ac637bcbfa091815fdee90336977b1b8b761f399efec378c4912a2fc3f5b0fe61c9b68c84652430c08edb1c6d2987a5ee5175f5d78298c4fd2c843f0215d40a22d7f856774f049dec182a96548dbbbc3c090e7efb5e0c39cb32343491cdd8ff9f8556d5391e80dd136aa905ced6af9dc94ec8457addc97bfa20387b28bf76a85fbc1525b4b1376be584b889605915c4e7d63da3ca0bc94c9c0dc51d8a72de4ff2faf5692e29f484be1bb7e463098343411641d5dbd193d8bef6074be96c58fcaa8d1084c877fb549cf8c4d3a22cf742553368aa22afde60198d5adbed9abd15983de1b1022794b2175ae6be11bfc819e48a50ecf994c7314ddbcdd68851b1326b49b093c4d96a9bce918480d6eab7403ac779c037b7710cb99f5d874ac78f5ef302c280d57927e51477dbf0608c77c81b7adcd05ff1532f1230b9745c6c007b4e5fb6ed1aee0e59903f26ecb9f399dd3a958050e07dc99542734d142da4a7f8558bfdd73840d18baeca2483e2dfbd6f11ce0a43102dea28f377faf1fcffeb25c6ec745b87869f4d0914e35f8a00bcaa761e46239416d0edb31811051a6957ac940f31a63805fc5f5b820de366c5fb7a3dff38a522b626e1a045a2115fcc89a7f0131d3d6aad2d33d4fd6dd8ccd295acddfa4e298411533ad689ce408d085a43aac9a1e4580eb9a7600718f6909ab4e00648339ce971290d5a18757451edc5ab71794b2d18aae8803534f3c9191f996feb6c6f1b95578424de93d8bf78581ddcf030de55346bb687143ab218e092b079967f130682c62b70887c7191aa6baa471381d82af6ce5678897eecab7e03ed8cc1afc67219f7ddbe83210d015e49e62ecdaa0b15c0c90b6778fddeb0a698fad0a5c60dc0013901e5350e3c10ff4a542d07f636f3917881725519db5c866444d7dfb1519d7e48bb32b1455c5c60a751922a01b73038561ffbde995ab4600dda8623c561664d25607fd727f69ebbb5fa692fd30cec313b10935a684e073536dd9dfae6e6ceaad249804d5a14defd1d269c44f91bf36086c22871e3d5b5d4dfd19ace6b6e710d924c57d67ac196b3d5b6e638cc86409ea27bd1a2701089406513713641e07f3d4ad5d3857d37e81b545b03481c807030386ba5bceb73e79a485a8e1b9063d84221baeabc4049509406f0fd7b9ea11a469bac8492b8f9a02732a77aa69803a50dae9f1b9db2e20712caf3a4cd76ff8c4baf8dbbb68df9baff71c291ed938d100ae4d4a21e3eb0d2dc490b1bbeae1d63c17833cf05e17a5daf138924041d33055ceb40d6cd0525c5d0c1955a16081cf3c4b07c06f6762c4a19218617dd56a4db2f914ab6efe6ceb9b5d2bc5fb66066312e7b58eb4e31bb1d78cb49cfb9532c800278ffcfdbcfb0b219ef67d1d66beed47d5e777d3646c8bad2ebf0d052600e1bf10f1680eedff87317cf7f8f772828b624d4764e7499361677d6e5c3914c1292b15a24fc811b9bdfd63fb2c9c43118e2cea256e6dc6cd470bd822b126541ccfcec108306a619644fc976bc360c6594a45f8e471ae238d1bf7f248e2e4429ed8522596f92042e20c298de2fcefb6ec0b063a4ab0da87adf87d3819b8ba3df82f280002e6da32a356060a63bca123c0bff700dd1f2a947d2718d51b6dd441002f1a5c8b52fc0ba2c607517a16aab04752f2e84be135e90c5252a21068dee8dd4035ebe32e9a1d3b9d531826331c7a9940a5835c566f79fb4fd9a0a99d621107cf9bbfbc748c316bf26adb4118dba789d2ecc37bae39abba667b55392402705f0b0acdd7577c1caaad7ce44d16eb4d68bb0625be63dd6dd5cacc39a5bd8daa442ed5d49f39d246da7fe4a33261aedf3313b9005a1928a4204e7b13bdaf5525d93d1598674f2172704cadd1faa7580c55648a3a1607f250fd18289bd0c56ac38d6c5d29dc8dbfb3b1c3e8a830a0513b964775fc022e4c4226eff36a1890addef17da63b23c95ab75e9530725e5e2d091cd401d1e42e867376458e17da9459340c9ffcf1a929d5b2665e3eb8b1f6241a730e742b2807bf7f570127b93724719ec47a612334610cebeb284ad6ee3d148eb2e5f09dcbf96cbb3477a18179d9b37a31b6a826b6034f691fd44b09bd1677bc186e34a2430e9559f710bebebc8c6cbb19fdee0e91eca0b46bac909298bca7306a91194a86547f81d2e12e37ad00f5bb9211e9aa682c0647fc38ea311877e99a46a3453590beb37959ca59649a7077ea475ad886ff55cfebfe9384370659af32d214618739136236f5410a00f7fa31150ec7dafc7baf3c691ed177cea80aa667c09ace5fd20da22e237204c7274a0d18ccf53cf6f063486e3b1ddcc785c721c2c215700bb84f4e77adabd83355fe216c3d3e1d316e1b7aca2a23aa6aad3cd1ecdcf778ca74a8cc7dfd6bca6da82a47cdc390a1eb9f1104070e900b8b0e465d3008bbe35fb9422cd25aef4ff342699f4e7954abeb13d7e2913d3a193a181c01a7ae99dce24bfb0fbbc4b9c5bbdc1113c17da1916811147f61d503f41e8f59d2b497d18ed2f0062e45e5e980d293e734737ad07b9a6325b964b17d9e403c9d5cdcd1eaf77937d80a29e8acad8989c6c4bef3e4d8230e46b1e985717654e4621a8fc2eb2acf7465e78c8078952561b70ff501bad3a67ee1dc4fbff53f1b097453d9285265b2b726afb16afec88fe80118be5ad90b84e0db2ed18c6d7309ae3dee971053391fcd18d6d63f80050cc8c0a6cd4e28bae18c3b37a2aded56b5fe44cc0a0a97b157b4862c32687d44fd2146548e44342b41df6ca8e9706d72372161e7cd85ef366ec2dc0cabeb79a7ff521550f13de241b22309da9f9a316f8231a11691f2b3b929fc974c750fc45735fc6bd3858cbd5a0853706c1f4ce59dff1f98d0fb790843e15ecc66c3d939a93429f9d973205f720d30cf5b025ced823782ebffa19c6bb5752b4f9770519252f5b11bd09bb6e7762abf82cab7eb346497e5dc1ac74196a42a81b6f7439b5d475ae6b9aae9290741b3fa2db8de600ed73e04ad8f8506f38b064f655086da936f2082ba6683e170b8bfabe64c152284ec26613da1d02c14327451b10c2ba6c4766d8a6f5da884900641454970f850d1cc21503d915bbe4afbbfcff67c4f44328531cc29cf5dc76ed48963aa0a959b8841ee59cc5114006559a9a06db264d1f4f94804886cf09e42adbcb82f2a41b7238874e70bf064252efc0a39f0265fb761b077a001a2c821189458a432eb73cf937195109247c87af71fe30bca16388929c426e7680fd71cc49abf39af46e08cd2716bc9f2d4c8051fc992a38bd88da2861727adc2ce40b825f5ba77eb599abb14a78402f9387004a1e2a8d7f4671215f3943e295de2769a82729ddc160d70fd3cb043f10fff46401fd10e6ed3333a656cc9f82276759f0ce1ce97f6656547d440b45f71f90568a8b8e917983670c88f53f2423989288201d7d73370df56e23f8db71cf86e6f89e6920dfe52a64840d414beac83834332c29e0f90fbb59506ebc5c4e300c66fc5762fffde511503407ea3faed99e891a7a01127417555620a35ef583b37cb4ba78e9c7fa6d0c1eb1ba4a11db3078346fd0b73d75a086fc3d573ea35d88187846ff7beaf26aec275ba6ed746f2749b74f1d85ec962751dbebfe14a522fc0dc1b625259815e95d196b16b421e617dc1d77fbb22a4d5c111967e439bc1f2d50787733e4b508b79168606226a1e3717356da8f76a69afd7db947dd2e34023d93b429fadcbe69cb4bce979cdccf7e78843c272290c61f561203d16811216fd8f35f1b096098c4eb4a634c15f78e84849c8c4293095ac7c2238b58dc3e00a2039dab7144bc4f57bb24bb5bc1c4b484c0bec2ea2af62d013f4afb24fe6a6c87f5c321c05184bc7e2282d0c7fcf6384bb40246e2994826db95f398b000f043dc70e4b9f84dcfd89a3199216b3014eedbe2b0e26a82f06667304b1235bdc72c1b5840009ac75bdef94b7bc56d21c68f15116ee895f760b4c394e553def909edcc295e10b1d629a414ab5f96c46d8513b1446ba1b64330ed9c5bc79f7465840fcaa621aaba04c0806cb035c0164f55fb6ebb4473383963ebce2dfc8a1e336f5d9b05696840ed4930acdf6b39aef98ea540fbe725f0fdc093364c19da7bcaf997c5a5fa95a311075c89bf7457dfcb0db6ceb458b546f3c9b3d7d59785a6a669bbb41df44519f6df5d04ea18a04b2685583c421ffca0a5c3204a293337a2e9db8f6d7b363f4fd1c87948f29f1af8c60f70e9eea169cfc35470ec6997a653622d52e77729bd74b20dfe12cc5fd84eeac1f2fd3946d6ec9cb3dd5541f20697868c5c6d74051f72b232ef76acee1ef703202140d9d1a5a3e6cf5b88a19686af3107c1afd5f25b2b1c23198839b9e7accfc667dae24e3975cb8a54d55776f31525d722ddbe199021cb66fbe8c5580fbef44c58d18db4cfcad574cc30b3d10a8863b64f1d4d7f494a6a923cf577755cbbe44ef8ebbc8f4eb90b903399834897d60a8baf326b70a0b1f320782fedb5cdeccd06888e0145c669bfc5e81a21916f411f60fe17b150795fd8c0844d582e0bbf874bfc168f7d4ef8a1ad27c4ad006133defc2b5c9cf88dd8295968ba108f1b198500ec6d203e0de358eca83415150c44524e47da2209bb26c932ea1d827236802c6491137164e2d7e7000a52b546f80216defb16daaf7b2dd8e5024bebb5ecdb7e2b0f1fd3c22c6b755e5e8a349b497501f347f99cadcd8933f414501da8c87775aabf8780416368c092c835fcc64f7ef240dbf4140537048f49fc23d071816dc60d41a41f0b6fceedca4d22c5f18ed8e947fc31886a4512652a0459a1563e0bc834c2eb4e914419da603bbaf94e45d9dcfee20c71ce00e461a50bd9bd8770507ec04b482946833e600467026093bb4cfaaad7c9c15607fc3eb2f0d7be3923a7e1d00335644243807d2d892f20c6ac06a6d6def29eed6e79f359845e731564f44064dda2d4240e40a40f36d7b0f8c1232a24855e149e9c2ea50e426619a83b1c5029fd0eb4a2a69839ec7287b7fa97b45d916e0257921533a1890912022d06f357d017a0c13e124908468f044602742b32e14f5577f698f12b7e2399e7e7070321a7893fe7951dcfd8386e50033610cb989336c16e05c83b64e79d8db52a1419bd2af32cf7abb970ec793ff4d95dc7147256dd6c63aa26bc43ae425a2ef463f7b24f1cf6391a9618a7f4238f1f0d08d92293ca3a8ea8b56277cb22c80ef9252296d12d943e32321e9b0b5644b32fb686c70a68887ec37e0a0d68c2dd1f874f83cd9b51430badda0490365e3723d216fa827f23462656a438dcb426b8f1796966456251805793aa57b5ae45a8c24116c6dd75c1ded55699871c777e9343be3013921a787e8a139b2b110fc95c15e93d433d82b4f6b70ad9c9a907f0c90b8b43bcb749c8457bb0b765b00c2f1e393334f675ab991946162d165b1608863ae35fe2cb43377e4ef01f60fb3142033183769546ce766cd1d1036f7a357293c2056d15bcc3f007f9eebf1f95369094343863d8e4fa5369a5751aa82468c4e71817f6d4d476384f60a54201e830388b8ff521926bea262a0096a22920a4b9140635289cb135264a7ed99c545e53bc5405f9c7f678bd7a76dccbc1f0ba4d21e24fd695cf808b8453ec95893646c2d31f9101f02226ad1801aee467f05cde79ec5ec125a7a6d6c8cb1a0d673c6e58060f43c0b7414c1e3992f42f0c43e01f8f93c9d6fdce253095d39c67f35ab67c56d534a3547f64d60d20735335cd29d08e3b8afd3bf0bc4ffa5832abfd18ac039cbe9e3d22275592f11998f89b17c050ed80d392c7107f011f78ee2e9acaf988cc81ca474ffe1bf85e428ee68afb27e7ad42715512bb6a3129d0b5ce6ae0629fa348464c6d10963511b9e9d6570b11a31f6e8e1c5fdc01167799a7ed9762f1c887d1f13c29c95f0b1e384dbcc196130aa096bc643be22bb0c1d47404a85dea2e56594f55fb41e46e1d5fc8131049c6bfa68a1e206743c5c23b71976c0c6e15bc4ee5323149a75e35362e58708b603d8ee30c62c83ae9d843fc14218e14fb7c1150c266af1f5f1c5efeb19a35df6a337d2e6c8b690436b78b0358fbee2dcdcf7590fcfe8deef7584f8e068baae1e31f265e4d562798793d3e14495f0983b5189a000711655bfc79360ca458767ea5143f99fc19b1a2ac4221a7b0a004fb567a6cc625fa75ff2b7f8145ec155073c109a126ac9d98578cb9e97fd4f387d6e99e8e3104c0850ff3d6be24696b044c62e27716c940fe83b5091394ff8e427850470c684a7d5d5d34bcf8bc60ddad26c24fd1e2b2fea2ac8ec641144aee674b3d6b241986f834195eaf9e50794721d367e5b76ffc1d90c7bf8906681ef996bf5cf576addf7723e380db8714e817e5022dff397fb361d826cf9fc083f0d687132ddd3d94b37301533c5ad449542ad2f090609dd7a4c78ff46808eadce7d2c88682d87d4309b534ea12b5ad7a421732b12e88e94b9795044d789948f3270ed25af30e8650561a864ca2b9a93c19e7465b603a4f375bf48d6a5c2ec0de8e5518ac649c8f399e734ce0abb84ffe5f52e4dc88c9a97eee1a0e3b622459b0f3f162a01a845ead8be31bd623055db6b862fc41e56f587edae47e16fe0b6e2c7916b47ed4ead5d2d46dc9a7b818adff2642cc3c63b5dbeb2bfa82c52b3bafc236b7da577473bdaf363d190bd86bf04b3fe51a0df30fcc00f1ceae9e6abd06e8cce40f64b85e393a24a4fdf9af9a016114a378a26337c33f7bfa1f698062eb6888072cb5067f852a2f41f12fbd4cc531ae49ed21757958b97c785c8ae4fb896f40060b82800100dad7e2c52f693ee3c931c0e6bc01e15a1504303215ab5ecce4e94905633b257615ecb3407256a4887f5846f00a111765c9ae1d17ba3f1ee6afc5edd8422195bc10b92e5e9bce0cefb7d8c5bbd6a72b3cfb6ddc34b54d190ce070a2302b833ba14b6483ae056f21efcb4fc1f3e295a2ce85ead4625805d58115cbde8953c6ae37cff82aa8d7c534781b206dfea6ac0f0d46265459da804dd84f0315b1b573fcbfcece43c1261d03470bac6d60cbb6cff5d24fc788eb21f5aa1ae844b55146563b6f40696ac91ea23062dc67ce2c3a296efb9bfbc94a9b61eca062fee994a97687293fc18901bdd49121df5fa43550f18f4e44bd0cb89a69abbc2f92087d17edbc02d310b31df24b618f1a9b2fe7f22709ddebc3cb35fd8fdc2a5953df77ba7c4c205802f081e7272ddaaa2ad20cdac2700acdcb5e471d9302f0e82165ff99ca4d55d72609bb5c3adc46fb24236d1f6aeb3cdf17a07547f92884b31b907e6412711d5b46568633806dc5056effc0882abaada68b209a401575b191e3e917c04db805c270aec963c7708029df57817ee594adae3d0aff1a9c27db0d30f135d4627fdd3f00c3a0bd6f945d239fc53c60b169182ac9d56a3afdd65780e0f1ce3094e9b9589b91c63d0c3566f62e5dfac4d62507c933dcde07055878e834e981879e70e09c05412ed1ab87eec8560d9bfabd8ca6e59d0446461cceb738721c0863de0a57db6922b229a1fcd01c0498f75650e56e43b144fd2818f5ca9334c248d54b69e88e51bf93ff12d072f46fa681d6900fefd429aea2d8cb1c36796f2df6dc969245813ed929a27de2a6839d0fbb1e057dbf30478dedff95415c75def2c4b676c3b2a86ba6d553a7f636adbf9be6f3760212d6fbf74fb6b85d07e27c62bdb3cd1bcd9d40ad00b62aa1ce0e303c5aa9a010d057d51f207049334a8befabadb6adcbfdddf0dfc9721bef2172987cd697076fe1ac7c909ada502a94867c4f908c38bad12a5b96615e82b9ad58e5e129885cc1b4282dc4c23bf7a056064de4a86a278ea5a46a86889116d7079242010ff67deae78d5827ae18e2eb42774d60da58cda14571c68672d3408b3bf93f4ba84db50aafa6dc7f336d0d8f80b2e3aeed9d340a764935c3835f00bc1acc89a161b21fa750e4c919ec3d602b6bfaed29ede346b089203ee9dc855d12fc43d3649f85e2b776dd2709785aa6f71062cec0defae9e90a16aeaceecc2482bb996cb037a3495e5e9f108c35b2ab0bc122534b2677dee21bad5b3ef8ac867fbd7989df09212250ad7ccf3d986b39cbf2216a7f8254baaf54129e64e60100919e458bbd69b75808e1d764036adafcebf255ad3acc750f088e234d02c5146e259874a53ea8d9a38f0c33c98d4b5e236a9e07fded74814d0685de09eebf3fbc0df02ccc18cd29dce0ccaeca756c74c4de9d88a7d4aae796f9231806b716c11a8ae651c9524c8053557810b4c2bea8b120926bc32f29a050be65739accb8424c8350d04c0eda5f8c0fa209e8f7187bf284f4c4e3c8e85734c4c1c503f108da31d3fe9a03d66149ba4abfdb8247953f03529e5b22de689a249550019eb03e47e898c7948defcb13464ea0ee43c0e24fcd38afbefb038c082e037d72df96cfe40161e368ed8766bb26ceec90d1bc204b792a6367cd87cec377ddcfef3b529be5bd00b9995aaf28504e6c9ce514e9676814bdacc53552296f53122ddf9f97c9e73a9fe13052e98391109d84687e2993d9f9a68d3da6b235014b2efaa89efaec629651b8fb2e84626c9368a80bcf59589ac963b0847f13246f909d9bf26ceed3842ad8f34dceac62a8d0120f1ba1a8fb46291f7259a67a247ee1286ae65f738108a555d721671100119423a58b33ddcd787abed9c97574798b08e4eb93039843b4f573e59019a3f1ef87428cb1b8a037b223eb2f7945a0ea0fe9a5d1a207a6786e5ee40ae8874144340b10dbead5472521afc3d2de9ed7cccf116d9bf66699c58739773aa16de134ba7715e47a25f74aa45450fd813650f5a71a893abe0fa41d12e43911d9d33594f2a768664e40dbcc8ac3b85a9b1d9e299f6125cf5e4c3c283fb95c051e5cb2c6372eb3627154926159fda835500089e119b095673ec96e658bc21285744023aa79b83ea09d9ec0c6b110f3cb9b201971b9c61771e3bc99af818069a97e8c1f48f82f90c6b0360c1009acf7dd6102b2eb63c34b7227b0d294e5adcdbc40447d2d26cc8a56cc47ae9706a78a88f44da01d578f266d54eaf41ae6fc1497e2dcfe5bd35b0bc6151337017509774be4b90809c4cb237459f3ffdc2f39e8284692d5f0dec99087843f2a93b7587ae1938d55a815913e8da8985837994f9f524de373c3510218fda2f032cad4804d7b76e4787ba90fd17270a13582e74994b7d9b9c92e16d61f23fb266d0de38102cea58b6fd6eb95c586580d6dfa2ef83ed78e49401e7e84a65254b230356d6c66fc8d06941e5b5f85977ae670c7884543612c8db131ed3a7e61e4759facf359ff6f9233edf224c8303f1cfa18677456a4c69ddccfb4a8b17c54dd6afe0a3d335017a58298181272405fd15817c13c40810d4b7ed445860178dee0362a71f933e82033f4ab37d4b30478c549a7b5d71ff6495669a1a760b41577dce195a89f40e1e9d43eefb9eead62473e1f2ce57992449a6742c4936d43282c7614c3f5863f2d63c8c383f19579c33eb461a4d87aec83cae47ee55628323aecf46c2c90c3b5fb624de784f6f8abeb3f74f5be3c9483c8861973b85652a0e79921eef4e3a6085a4bd73035d4d4f3dea8237fe92152ec616de632eccbd863e18c3813cf559d7665ac7a4e5239d3e19bb2ee1890330358cec0dfb53cb958f06102d3b5004ddeeb571c2e840746cbac64e8ed7715c39b1999a96c2d80caf48ded607274fe9aded6814cc9f1bfa47f98cfb91b440772b267ff7e9afbc5e1a529b7a531bfa8512b9ecb31555b9ff124e71a21ee25a82643a53f16ed7fdcd8c3eeb2546b08627b800eef434b0a82386304daef4f2d6aced68af63f911c7f71faad3705688d8df981b320548482bd04c5ce5eb45639e9f2b963623f998081531f98312ed15117b6d8eeaf8a17c085101a9797d0be008b7627646a069802802a2d1e5ca4a139538675c4dd26e01bb08883b9ec13fbb5d7fdcb3a3160135f2fa5e497289a5590c01542c22f6490f759c8cd153bd7f4267286cf7a2903617ff7e3673f90d2409d7b62eda8e85071c7329efdbae783cd4a29a4ccbfe543146d354e4cc0e1178c8bf335500b76cdb42e874a93db45a85fafa298d2a30fe2b0744e013464bd1b04cbe79eaae4dd30a245aeffbcb6fafe853d0648c867164c43875e9c63f5d0f35d81f938b4e2bb08e991e5617d9e01b4302dcf2c99f7d325971e3e9f7adeac133cd3e8edd9c6b0ec238e587dad74f5530703f9c99c12e9ef7d75e8ac8132de44489a01b850f1056f35c5b1ef6254005e8ea1e80a14cf95ade507681fde2880d97ddc6d282c7623c4f5a5dbc907056f411e84273d71b4b42e633de18e0c3088cc48913f0b1654298be759470b0133296f7a41f9f29d40fd1d29b6f8114b1c7151e4a64561d9ae1cadbf358bb15af4ff7195aa9d5560abbdaac382480ca375d728381cfe4e2acf121fd2a97c9e40a56a4e0359cb2ec03df1d72a8f156379032aa5bc90e39a66083ed82001906ace7bfac01394c8ced23428b50608c95c1490038b995dd933911ee989dd5a63df504925dda036c22a830ab275eb28f84c5bbd02acb4bfbee62f1b4c34057d67f96e85afcaa0dc2011d0dfc2d18e85c57bf29458fe4089a86fa8bab3a5b9bee181bad0a2ae394abf9152c62d88608fe4f6e2099930f0d7402fd65e3c5894f68a8cbd346f4cabf00f6378cc898756b5aa5d26c7f4edff7e473c4f32281ccbb319e3104eb38d46bec19889fb99c4c464a1ea2f3956852987f4b71feb05ba807b9d5a76c6d0df1050875b3b57c593bdd463b4da1a6387589cd2debc3846fa81ff09c7037d0fccf01ceab2a280c3bf317dd06318ac1864c60c6f392c0391eed1009863f0d27569a5705f6a8d09fbddc3266531350c40125e7437a17e17db24cf6f0638f1e25957d35426559899a29bad4f860f1620583d896c1a3a79c985944e411c86ef1ad806087c41083c08453321e720f8baaf4f258beeb4baef2677bbabb71345c0eb7e07b703cdda8c90c6d9f6a81760c570b57f2c004e15e641515823b2d900524c1e38c863b919cbdac5c678268e5e105df110e4678a0912f3d2240d8cbd1182e9bc6d5c076f1802254391aeca360fa03d48a92040036b4212b3653b3659905ed5c4dc40ed4ce4ef5804a4b784f4927408325fce14af3d45d0f0ea55a949fc06e71b82a602aa90f68062a853313b5f5edb9d515daf33e1e206102d55510162de45f0e0b7a1d60ded788bbbc3af9ee9fb187d61288f2c19f6a1c04394e0b0ac1ae9f07b762c736da22cb5ba12ae8a9f099aefb1d8fb6921c4a5f3191e60e9bac24c696644ed7019a0423444710c523fab7396952addff94278ac1f6445fafd4dfe15acae853a736126f091495c463a6f21688513a153a5b0352297030ab2695c0c0fc74c9faab5d9f994d5b9900355530efc8081eaea74cbc9ca285d910476991c3becb2d003b67dd1bbea37b28ba9053589836ada35f3104a90b4cc0bb54ae51ce368a8f92a27e11850c0e3da5ee1777cca60353e1eb4c74856deee51f3552653ca4aeede464b34e722608adc7f51aa13c0baa8fb3028171232bb7a0b27bec7e74f2720234e4cb98bfcdb5e23dd32a3b63e8a5743c1eda343e91aa79742a6c7ddaff54a121eb1822bdc8ab3869c4f4c0a5329a3c6da0f72fab9799fa81569574fe7d1e0ba6098da0d7ef3a4eed6c259ff3514e310d8fd760a32a16b820c496340bcee9715f623ee6d26c65ac82e9292245679def436349a5f0ff2e78a76d7dd161dde2787cd868298513f58b7546b38632351d9c240812efb0643a11d24a1609fb9c6af7560c84d1d331072fce5e19d67109220c9ee8cf94c2eadbd3280a56d5684314e95d7fe408ce6bb5650ddbcb5825d8c2479a369d065e5d94fb725bac5af0199f46391015e41b647221ba439bce497673ea6b305fe8bcb7d43f8aee8418545f0eec246d1b014a5f44bce1c104cb7f2bc5d282b723f2a3003e23d0439fa17122260173219087952f900677cff70f96d85438524596681fdc046674ef275c11a8349dce8d4c6d3c42fd0ed4bb50f52e2415dce4b2e0f8f3f0185d86167405056ae03d76ccec740e2e659d22abc8bec28749f54e4f1c05522b7a8a3f58068c101b0052b4efa77f5a1559a310e6c876999a53b107292b33712ee02999a22269a273938980543be791c649cf89da2b58c3a39b9712c3d0f2b98684a8b5e7a8f82b98d4f84227a73e73d97d7bb7736d1801acb9df86b02c0beb6331245f6ce73e11e86cb1dc38b158bbc2742a958fa412cb26fccc29bfa5c53721606b85f0efb88b4b73151708d3051f010e8c2297ba8c4b5053306b22cfadc31d692775d0bc5a1f548a3b497adea49279844977744ee735609266988b680ffae56b98f97149da659bcc08a6cf8bff8257ace00703655620afd32879de87c2acc8b8ec2bcbd3ae352d0ea64163b4f1500cbd68c4b3f58d2900c645fbea6bb086b737472fd7e04ca72339f9c8d69f48484e833b89121f266f13333ca38d099134b0b158154461ffffec970b298f510a0492415966eebb2bbae1044e01a3cb3e2614b69ee94fb05409f45ef5bb215afba6414be6d5d69da4bde687896e5d3d2876fd45b0b194506bcfb831214b1e254cc6ff71761eb45f6b3eb2fa33f354a20ca54071f37698ed3df90dc4c34d7dcf3dcc8ad49552c2606e09dfcee7577b074cbf455acf461b16bc8e9fafe6178b78382d8f5b6507144b1b2d1f5a513494ef7143b0484d3112fdecf7ea79d8904d9642c46315f3740fd3c62be7968bb6b2d93ea0dd57d30c4ce75c8a32eca486edad91aee2063db73e73af11892c973ca11ff72cdc1327fdb322ae6d401de2c0be0fb81ac0c4ca312876ca5f5b2f00ac85213867e0e8f6f14b4a974eea5e9471d1cd4d9272412838ad6e34d58c4bfc57aa2273d64d599fd88fed81bd221344ea4d0435e4aa4687093984573d3c587af16d31a3dd6938f38e13513c423baa36d3df4edf5644712165dfb674ae3fcd329cb982a71e734c83118b60c5425ee7d13f03fe9f8f56863f4fbc4ad15e14c4e1807c9dcaec30b751fad5ecc2fea7c55845c0a57c90642fe6d42b28d5b015bc82e07732200a50d3e8aaf5b427f1734fe0c410a383d1651c3a6481c660557bf13c4dac94780252a72e6ba2290c058ccd03e16ec604ee66f100d4a95aa19f099dffc2b8624b814fd5dfd305a61d789dcbadea3c913b38c9fb79748b4f96271c66395e36dacfc64f349e3bb4ff4a7d3ad3c237")); // 16384 bytes
    assertEqual(ev.size(), CloudEvent::MAX_SIZE);
    ev.contentType(ContentType::BINARY);
    Particle.publish(ev);
    assertTrue(checkStatus(ev, CloudEvent::SENDING));
    assertTrue(waitStatus(ev, CloudEvent::SENT, 60000));
}

test(09_publish_structured_event) {
    CloudEvent ev;
    ev.name("abc");
    ev.data(VariantMap{ { "a", 1 }, { "b", 2 }, { "c", 3 } });
    Particle.publish(ev);
    assertTrue(checkStatus(ev, CloudEvent::SENDING));
    assertTrue(waitStatus(ev, CloudEvent::SENT, 30000));
}

test(10_publish_multiple_large_events) {
    Vector<CloudEvent> events;
    events.append(CloudEvent().name(String::format("abc1"))
            .data(Buffer::fromHex("cc175a067eee6bd20af0b94849a81a628614b87d9a4b6a7ae64afceb66e75918f7b328595de4d6c3634e88f0bf1f2d610b2e559d0b9cfbc9ca9eec58ba4bc98909385ac4e7daf3287d8d558ef47499099e9e7d39240f8b170277c77aaa6a4bc0a1bbba75a16d1578e366712d75c851b4ef7a5e2f63bc179c5cf297e188f3e159d6f57f6a700c47a00e3ace580487a38fc3056a4470ea64ec993c0247d8385ae2323c08ebc77541c7813249be2be39fc5e7d94e1e10cd5a3037aed8c79e604f7dddc7e27ca25a2e62a23d55bdfbd4a26931f4de9dc866a3c1e81ca38dbe5406005a314984aa9461ca6b505f2e60f926e7acdaea9fbe507f9b9fe1bdabd335578c0a962ac895707e6a11823fb9dc1eccb23aef1fea3e99f35a4da4e0a0a13f1c6f17a74b1ee9b239eeba7887c3cb29512f2da6572c9f0cd1f6fffa3862c6df99aeb89a270ff3e69d3cb0df3c446a2e64a815fdb9254d02a0ceaa22b08d7b3b72404be40c5fa4a0655e3d70ea08cb8ea000c81594b0a5c36d02816833a46129ec0668fba21c45f64d258b4af831c86887b1a6e876d9b34e96184fe620818a7a3fc8968c0e6ee052413fd53a122aaf9a58c0337ed16a639fbceee75335e6d4307265ed03e10b46487a592c55aaa94d4f88b75ab34d4c8412696238141f31376500a52c66031d9039401392f0f4a89e287aa11de9674791f1e1d9b2d29ee19db12dd3b16749be7add957911d7d9a68d17709ac60017b7e7923779b6136aa357bd4525e827cd5a156e70ea4864eba6a12e0eef5611ed74df199ddc83bbecc77708725b522ee01c736d4dabe4df3572890360e2b399bf424dede8ba3d3d9c295bf8c700c3c0ce1f71afd2897c8fe2d1d949c4827014cb00296ca3914a07c858e47e38236ae77effcef4c358fedd2e771919d7f6f657eb4a243b9360dd81a2926c7355f03b12ab700f0b59870439a55519537f9ab3610fdc5da56c5afe158fd5199bc9985051b5e4d1c4da3feaeb1bcfdcf7fd00f976e224e4d4660e9221a88b31787d7207187daa05590e7632b8091746c2e530e99e19df84cdba0ac8643a4d374713699bd517df79de997789f9c758318c5605360042ce4f5d70dff39253f8d7bf750c43ef823de3b8254b5d134c1d60c94a0ff772e04ccfe56c6915015cdae396cabccbd70285432772f40e0eabdf56f15911157b3adcddcfd4f212ce997a963582e52430d9f0019299b1ec14dd731a6a406b33f9dd5e63b3e02f026cbcfc9385d3388ffeddc0c856ad3ec8746f8d0ba5d67ee5aff0072b51a9793d75fcb8ba0e223524af1ae8ae4a2c7cf7f5646666e92c6793b29549c3ba6ca422453dfea2d4f8deafbb38f2bef0b16538487e2f7fb8b39ad0e5eaf2e39dd4c77b67b5f35fe908759b7eee38359a2ada1b689b3ae7190109186dcbdadd3de6a5bba5f3e9f863be40171a402aee2e5c429884c2bbf5a4b075951ee49462cc2814b7dd28b7ee9025684512192fedc132f97db7fd912d6097cc4cc33240d83dc56eab0c149be761cec3d91ea1176db7769541e25e576332d0554cc8ffe43ab9563bdea5d33b14252c893ecfb0b5aaf2f74494a948add4023164f8b73621b33503e28cffa951eba90be00cec4b05f62da32b9f712708b9b2f9c90ae3132cbfaf9961eae6315449239eface42e40d2443e614302b5e54f835bc561081db3a9ba024c83943e04627e6c73dff4e634666f16da8382f90b7b5e7da59478950890f60b6fd18631460ac01ef9471d17110042648ea508836e7fec434773aa62224353ff74f281e5113f855a465764be75157816cfb0d1c7b6af83764c7f2ae1bb0d8b9f1b327e422cf16b4cdc880471bd18d562b7e86637a2978a18b981c6401607304669410e0960e3e706809a8e81789dda0f0a68166ea985ecb00b499c59308fdb3c70fb9270d221923ace7400cc6d77a7a0fa230726412ca548ba5d8a2fb0bd3ffddaf3b7368814688661c545c35340f361f246f996a5010bc460788f5c25f83189f809cad7a31d9a61dc1ab16f6d66b11097407622bad4cf2fff9137c8ee36f18e2290bda09fcddac1224f81a18ab73474c468360965287401bf96c9d81e0af6ca6b991324cbaf68c533eb428b5fcbd888df7e82d6376f3fbf8706cfcc0276229fed6fda0d2eac219a5e251383071fc0a7b207dbcafca858373de6e3ee7659e7bdccf7d526f2734753cf99359f99b52f1b73e897959e8bd4e3994da6d027419ca7ef3679193a128bb2ab41787b6619fe2dca8c161ba3bd0297610b85358a8b4ca9411a345008a601d4283c0aef5d1940f617b7ed67a307ce8ae3b2ae267c1384f412f06bc7d36155278f986f24ace71aec4c52fa9c8c308d8317f2644d71aa3732771833395665a04ded657c51c266779c415e7e38087b1d57d18fc07bd17415d94cc5db51597f0d94fdcb138a4e239891bd94a97f2a05a84df65396a79be2b2c9ec3fff12e3a31b80070a779f3f78f01104663ed50357762ce7e8668533c5461ec888dedadc55a78c362c755a5a90e0e560e5182f1ec76b388366ed9222706700aa5322fe24f6ab5b818590a03ebb9e6448e63f0507ea94fdc623f513cdef864455001843632432cb0e6c2e9369d187a720ec83db97a30b40383f52e5e146ab6eabd3c4704cead11e0d50cc6d9ebb6ac23ef29c9b754c934311ed6b009feb2bff4e46bdd02d27717f26bcb99632143e96193df08a8a1d01e5acdcf76d7b76648f7495ddc3ca882a0f2ba9ae0cd100639d49a3ecab7d52eb1d4ac1c7664c3a746ba8091e79852f93a83c93442660c103018ae4d733adf2e12785d0679d0b50e4ba26b9dd875d926fe09eca8686446c716ee2ddb8f9f7ecb53b41fc44446525b960684e832e6b5818146ba77c77902b6c1f176eeb9eb4285a04d13f95de51d946aed251d66b5324e4e792480461c04aec2d4a4f58358df9dc87724497114a05b7fb0acf4924edb17706c06ea04baee79eb50fdada7d56c21f454de4adb41d48631f903ff98558e133e8879bb29dd891a45be8ff44be93fa312e89b40d81217c20160ac8ec794e8f5ab1135cdb3698e58071258617f2a04bd6dc68b42ed1a42da51167061000f1582cd6b5aa2440d54b3e9050cfb2706754f91c077311badb8ee1257064cb90921ca35c85ae14b7237c56b06d0b918615909848ebafe660ba819120c2dd93ad96f839d43522d65b757517b1add1c58f49eeb3d6dcb00421d1462ee82e80cc7ba02a183349e11298e1301e573233759759783632d90edc1ff46aeeb5da961a12d769af684afafb7302d636b895c452e99b1ef8b5c7c6ab0ecc3de65ce7a5844a4a559b6047b6fe0e1bf07e5a9e4ed801da8a2efa5a4d29c1c15f64b7e384665ca784997e27ed13280e2a50b8ff9d464d2af7e9e876a9787baa88c24291c0e17c3b004c7f20ee2c41f9c7f2857724a25f295e37fb47dcc63d1040938c9a006a3bbbc002dc92c6f170017b1367dcd695ce03f052c8b8105527911a295750d3f526c629b6523b358b2353759d343272be74779725f366f1c4501b00959e3495a1630fb87ec9846e3af5f3e432f2d1e927193a7445c15483058ecf5719087a87c23618af94e3e8928e85483c8ca15886ee2fe84f9dd825f37c8f0bad778551776aa6cd0a7b232061f065522c0eb7a29fd30d6f2b284d453f9a3238f33ddd41be6d528addc2a5526973fef5f7fffb8f1bfcbc95cc7932c9e4d9eb03954d6dec10e09d56ecbcab2bd67336162613708782e04fe678d60d0518d10f47fd2b98f5420a3c6d39b414715bcc77a974a0993ca53c91781c54dcc2befff88c0f0ff8b35c3813fb12527b54b4d6c17e34ab79f9a04d667aa7fd5ecb2f64645ac001f76e4c71bb5e2930fd74ea29ebb73fdf14c6c18ac1aa2ea2ca2adb6e6eccfbe5041f23f08a9aa4e2ea62c14014334034b452ea1e750cbcc8b43e5701bc57be4cd4bbd39a959bd9d88d47573a95f8d969e0973c7c9af7631d34c0dfb1489060676fb8ed763509116f43bd912e435fbbfb5388274058c2a88f71489bafdf62fcc02762096321fac15372af1041b5e7e14bb900fb70b6eb02d4648a5a8eaf1d504fbb01d87ede8cda7c2d413437eb4686da4213c25d3fa94853d836fe696449cbba0946cea9d7d710a5c6348fe9b019bf710d1f530fa9645bc87a2f298258e741aa6def77ccc5070dfa3f6fcc9ec45761a3c5d911e43f49c30d31091df308fd369cf950cd6ec9418da6117aaddf4457e1583c0725f7dec3e7e08896de8696e493cfcc3f1fd95eaf7c4a55dec1ab36b3a6b8d84c43e436d97dd7e842845eecb6652b11126030eaf1c507d1c20")) // 3072 bytes
            .contentType(ContentType::BINARY));
    events.append(CloudEvent().name(String::format("abc2"))
            .data(Buffer::fromHex("bd97c37bc5eceeb853922c891614cfe4b927880b3ca16ba767473ee7c1902f1d5e38dd41a648b30ba64221e0e047ea97896b6a19d98d103967900b0db9cda4fbd0baf6bf6b3dcc56c46dba364379824fb187721cc60160c2bb7344099f66c7a015db797c2b1f44b308425506d1ab9a17d0aef322751b65b718e3d1477a2c7e58b87798969c1b130e995c00d1d46f811a48dc3918d0c8cfe2e29d1c0ccd7ade45c6f191069fa53f2d6d308baa9b0eaab8924e3f489206c76c3ed38b43fc959a01af93bca2eccd814d1553935333a6e57b38b8f9b546044ccae0feaaa6b2893514ad3b1fe4c12f5ccebcfc93df41d3ab7baa52bad871fb8106f149ef3e9ea6ca2cb837efee49bd1cdde4732698f0f723cd540c572ff9fc03a204713078a84c75c9030aa0967494293594e8fcfc9fd9f561d10e82023de4ca2381d3d64a3551bfc718d309c307ae4915661231b63fd5fc7a11ff0511101db49e7ed1007df9e88131dcdf21994ad544003eb5c0f402cb210da22c251e9da15fbfd2b9b8779e00e9156b6ce04a7a55ce68206060cfe808703a9f1f458d559c818a96260814d64394cd0157871353192c8ddbfc641e53e898d5d4862c34b03558cc3259aadb0358db265b2cfd4045e45fd31b49b96ad46023ef1489842c3f47a9d8e976e79ba77c0d007c54c443cc62de4931dec70f9aabe0d678737854465ed95ea0b7058e7e079e074af1ba6a98b4f7e9c952e625474dd607d4dce92b435fa4532ffad951736dc1e70f130509bb3ec7c9a41cb9d4ff8fc14c522d9d6b8ba0af444717f0b1d52bc7fb06a27e4a81085d6a2935bda5187b9e19dfb00a9bb12efb089b51dc24aa72aa82ea979e478321557d05fc9b241e765202ec028d1c9dd03a6810965b02f3ca731112786b69cdb096a86cdc0c26ffa74a0f764fa3913ee35ab0b1b19eef0c2793f9830e9fb97fba160ac0f63cebf4ed3fff5d60fd21296d0a05c7bb5b9a6d98dedc77138b5cd72c6635f0e95bd780f89a78b555193650cf04b7da04acd881f00949208b54eae4790a97941ace1081f2e5adc0055b2cc67c50c414b35eddebcbc49eb71234bfd412ab78aaad7c8072bb2f840d0187df8410b498bab4245d9464dbf4fc5d4bac109eb33a82a66bbebc20835d6950634c4550ca127bcc8cb531d5d0e87d72383e37586cab3e6d2a957f308fcc7b77ebb3e3cc2e07db26c2f352a8c00c5f8b759f2fca12f50ec4cf093c22869b8b585a413c6e92fddd6046843199e2c46691a7675e045b405c0510845b784d5ffa3313056865b4ea7092411777175b442b0a4d46137134bd4aa887079cf477378eec1a6defe92e7996480d91592d0e797842a089a26be3b7ec8371556f0db43bba36cc8d0c62c24c274e508fa26108b8c1a8684cd7b217f2443a836c6824a10717bdb7564a6565d25ba5703cac4e11fe3872e2ee697e579c8631573d4f281ab8acf2575e02134d1155cc59af46f31f18556ddacc168473179e30de26b1f1ddde271ad18beae8b73bf24d86384c4cf8f48f2171b6e3dd4d31a70a5edb4d5e13a717cb7749c9c9739c6ab86393cfd6416006532a711c5c8aef1b119588526b8dc3b00f95433445f7d5ee3eb0e29e85e1a35d6561952abf67cafa368d1e117430d61fa05414ac272d44cb31adcc1476b80cee35f9b2dbd60898bb52449b9cd207bbd6ea7705dff061ee94e61a8d7163ebade59dbbcead52efb0835e3a91c1feb321904b219267b3d41f9047f11786cc89eb9d18adf69b208cfa24f65edf4ea9db4afe53f03dbfcd0ad6ed4986ce69e17c65650aa09c6c5f48268db8eabdf74991ba9666200dbf2743a51482d157de6c2d71520bfc90adf24b204f5a2d0edf9cbb35688f5ff8b1858030c17857e2e1358e6e3c911bb9736fdcfe82ae1143dfd6507fc03e58ffe3a3cc0f0ecca4a8d2e4abdf4dd56d5ce7a1fe084be48bb0ac87c59934dcdaf2d45a3cc23cb6115b1fae7499d8d7e5e333125721aa5dec28fd377cfaab7e00afa7304b3fffbeb5d7f3b21aa58b0ab6bab5dd84fb28a8d384f4ca934399fd0f4718edb21931bc1a1e8454acc7fc29ced03bc875744f8fa3dfeff091e48aca2d68eb1a7f68f17b758c147f08e3a2c6116638b748e5b36d8495907061a54b2c09d63833e4a883b2d16b92a4162563548a330aedc094f8e94bba4c4cf895c2b919817190d31d2d8e2d9d2bca31ff55eb6ea23a69df0e241352bb28370688e9ad9c10eeff5e5b807a4a543fb6d31c8c439792514c0729e259f1ee76e4da2aa4788190068bed9e49b027e311172cc0ece2bf19805ed65c16b921f0c9c9936fcb0e67e8a707e7b6fb4b9f8c7d3d68a4e6b49b171e45b48bf039355cfc1a7239881087d2432c84db799c0f5b8382f42af04ec3eaa9d022bdc7da831bd130ef68adf23d79d73cbf24eddfd4e060604daa00169ca81c9a2b552570569adc10594cd3b10619bf1a596ba2f538a31091e3ece6a9dfc2540e99d5520f516f16949f25827fbc3c2086837f03a78e9643e1704d6eb61a92101b2385a347d176de1eb32d39d85f180417b9aa4f5509881657b233935b55cfb95d0f3df3d7dc7681a879e274b5f9e5368188d6ebe94ec4e3feed046e87fe3fd3364aa49b782f1940c43845f1f96befc55599ca88d40dfe9887891baff3b1ee562c837ca647747e43a29ff37d836fa35321f4b06812ebd58a44a39fc3344b8183675ff49cf9b3b2927d58e3f3eef2f58c9d46e724aa99439c07d1ad156c9cf7883a68a720dd6705d6e41672a786eb6f87f3fc5b0fcbd1fb84949e4638d0559f323f43c01dce5a49c730d691101cfbea841c0f9edd57e49e2028a24669d82703dee503ace0390aac2e16273f9ed5aa77e4edf5839e22996feb3b564d230c9fd9e48bc2a18e29b387f1a80bcbd12db01343594cd2517ac714a72ceb6b4b27a9168e4a22c200c452f2aa27d065613ea6203b87e62b279124492b5d87364cdb1974866c8961f30b1a73f270feec9e1117245d8ecee4b8da3f2c73d13e4505c049a7009fdd912d639e93c7271b229f82d36274be71041d06a4e92829ca6404dfa338d2120bf6b51dcdbae4495260fcb7097aac97a95d8e7f2b4d7307545ccc704b3cc41d40fabfe9a103ff4dba848cc3223107e68d13ca6bd76b559e5218054a78ac956960811bfa4f5f0b267c8b9b6b7399351a41789bfd8b3877f68f4aea988ca6c96517d2f95946575a58c9ec88c6905ec4b1b41475c0ce325e6f8d1115334feb73a1cf755b462a2fc07448620abc2131af20b00a2fa86b5261eec6855d3dcdac82adf4082c6ba9eeb423b9cdc4c2c6cf6cdae43054d26e21d17310640e58773ca0a0c83765e81b0d54f05d6ae28fdf3e60b099325ba292b66eed7c6b103cacc1e0a4b366fc2a23ad2ca34434234217f3e739523266fe206867a08e52b68a36dda97283d229f307374f7cc7defef3a8a2d226515fc0508fbde12565f861f6c6972ba4b99ae599624adbc93ff9e6bcc7ccd2ae3d313f5aefdc42f1f9669dd76377d7be2d5cafd98bddab396791024b1788554594a9c7f41810b5d3f9f0d78ab176dc7b4a9ea397b67c01559babb4b2c259075a5a248590749d7fbf7afb95b981521cf86ee872176c0e2d77e25d6789c64102b02a873d6e838664e83cfac8f197af89fb0467114e85f08dcf8837999d0822bea97e37361279592dd256a36591dabf929c6c19d26795f6d7b02ae6c8c76a979275adce8d86994cf121404ef999947d0327f53765cbb52be9a72de5facf5c7679eff959706b88c0de594c132422428ccbbdc3dfe52a79d42aae584e27636be9af52194edb5213c13f9b95a71f5cfe25a0ac166fd57da177dff645a8abd22710246df84ad26cacefb37f88ad2e1202be17e05b0b30546c327a86f64b6c8ed2bbbe3d5744ddc8037b7de89f3d85520ac11ec23aaae7e88c014358482663385064fd844309b7d590c49fc3b0d1a698f4d387286aeae5b005ef9597fb6eaec93e2cd9adf0dc996cb8fc2d44466e9436d29f702fc4be416ea81c218cc70d3803c71c492a167d796dfcd68461560b8bda9e3dfe0fc5be68589fdbf8fbccdc13cb10405096b826dbc067fd45e04d7e23e2d98743d9f4da44ceb9a140541d1dc24536686fdbdf834418d4211fb84ca01bafcb17b485dda8057e086f18fa150f0f2ee0911111a77f377e9a7ff2a18370fa1cb12e749950de49f545775b7d845a1007cf5d24abd421f93a0d64b29b8d387041f2a5460dc2c97c5eb1f3a7a2ea2ca6c8b8e5fcb1c1a6b5ba16ee4288b1feb4faa564a5ea6fd2864a57ef73ebca8ed55f38706203b455714fdbcb37ca3b26")) // 3072 bytes
            .contentType(ContentType::BINARY));
    events.append(CloudEvent().name(String::format("abc3"))
            .data(Buffer::fromHex("7223881577ba8e7fcbb695e76ca8b950a1ab001006f6cab5fffa554faa1b86e534bc00f34e784c909be807d2dcd9588922df1237463e7048ce7fabeb9b18ae1037f5f1838c90fb08a54674da9e2a615aa76736bcffb7d8a0080421abe1015a3ce55e9059827ad3e9f68e03af009711247f96a07fb29f7d9f11d601e9a49f89364184e9a44bdc9fed62061aa06437114f339e1a3b58f385a479f5e98d495636ddc9a4fbf45cb84f6fe385318f09a1adf768a74f08cce3886db139bd4ec2d19f405875c646082915048d6716b1cb73a477cfed327a902aef93d0134e92f801ab1332539e91348f3bd8da90dfae1d029f53ba4ccc9faae33688b2466c8bf308b41125adbdb8624ddf272aa7f1cfc34e6e84e726611ba9236cd4155cb9a4c4f58d9487a9d700db30a43bece5ec78080891be1024b1d2072721a6f82b210eb316af781754f11efe3137935e15aca077c80c91ecfc300130bcf4217ffbea2cfa53efdc43585ad319235cb46c637d8f33c05022dbbb3b54732f7caeb22afb938905f3ad77dff75c531d038149d98b227897c5f2bc331b3e4c93377294bdd3066a9d981139af9c37da37c624e57dd799f77392e8dbd96f60b05c0964ebfde1680e8b200ff1cd9a082f733b5a1cf1271e64cceb0e3b56d0a8cd051a7616ac0279f3c48fc03fb77d3e88191fca840a3657045fb2457bb2283747ba37559f111f9d8ecda4090ce31cf401292c49f5b02f406b47132cd8e973b1d6c82c13835dc8dacc2559b36311c66fcadf48fd9b45e5a4a25c0a6986915ac841d642d40c7b9cdbc0cdb7bb9b9d3070cdebba979ebc55d18ae56b26d02f5a99ad073ddade08cb8bc880d93c369f2c75769c165f8fc54e516fdab38208488c86bb008aaa9b8768b904c34142821e0af78a51afaeac6eff1c318e6b35c4ae4669f6f4430a9a70aab4bc489e984b9f3a2b69a32e72463e6fab92086279280252cc145bdaa76dd55decf66863c17a55d8a4284ac9779aede4cf62502a2f462b52ca4d071c0698da752978a6c145659c4b86dae758f33b32a8c400ba0469bf947b50e32aeeeb2ed79e73beffd393ea412184201bb8d3362c92cb020aabba35e3ac9bcc49d8dab52d5dbb2cc966ad716193d93235094e5a130967a016e6d89e2d064a73f3abfc332521b94ee24f9704c12b5cd33e3c26166d3e001a2147f7b3a79879f68496cc00f14f1465b9ddc00a0c58befb0dcb55cd90f57993612e625c8e635d2d6a8293bdfadee21e5c9638754cc288489cfde5ead9502ef81b5bbde703978cef0b1eddfe23f8f6cbe6c08eb3ccdbbe08fcbfec7d876bad76d64fe0e75197801a55299b24e61dba95869e7cd4244a01c0afabffe4b28a173bb5c8730af0bc289a8ecaf566bf4bb7c197ec781f8050e4487a030069d124530eec524af42a00d7ae04fe491408b21d72b1daeee7e4d5031bc4c2ebf235d1a4ca37262f91c8faa0dc0dc426018b8848cbcfa7cbfaf58dbda7bb81568a7d5e5dfc78daf04a0896210fd6ae0be9c12f6d0f95d26a62eb09419a4a188b4a47c70655ade0220efbf79818f57ae4ee1132f90ee4f497a030d97b637bd20c40a98bd77a99e9a682a0b44ecb68c1ee33f283b2fc3df08b4735bee93b123bb028129149fe59fff3e69dfad90a32af1ee453511f8ff77161d0f8481a8cd4a73aebae2a10ae99e49cd835fcdab2f06e5b36806265c3dba6a0f4eac7e2be9c14f6c0345fadd78684f379c43658677a90c4326e422e9283d665225fec0a45d3223ec6b445a07b8da8e11d789dbd5a9a949e508fac64ebff3477a2d14579631f65271957d7e8d3373bfaa7262cedfb4d978ddbfd8b4f1d3cecec9818c8a8b8fb3422e6589d0a231845859d2a5def89df7d409e02782bc4dad51abdd60c8d0bbcc31a4bb3c1ce7d84bba0e733667f2f2ff46410d31b3f20fef1114854a43e821d64d3dc35d8fc5f255f288035ea0d191b2d58adfd105e9ac0a3de9d9904f8c5787dc0a943dc603f2a7551c02cc39082eb7bca0d30fa60d66ba580887b58e9ed703afd349345f2931d80c79dead2172fecb584027199db4ea3659a5bad0dff21131a7167a4adf7fb51fdbfef97c8ecaf7cc072df32582db93e18fb3790addb0bbca55e5f8f365dcb2f0f398e1a3ac3ce4f552231ed0f172fc7d276f796f128de25c5da22aac62e38a87248fded3f00aae861e1b367e7c2c57832d1b4d21e4d6d9ae26082ab8905cc8c933285893c324169c5c7d19cc31bc489d9eade0c8416d5bca5477729a133439ca982547869eb84212804695b70819bf00008ce0beb6d8472948a7e344d86b128f15d7a4d0e4ea0a74c586c77bd592a825007567923c6f217862036dbaf48052a754c734adbb2a21104216f799161724631f38c4b9be0a5c07c5a2586cb8935a1d87e68492ac9573c81e7462acdc4f9e034e6dc55450f0e1d77036c82242b2271b4131770e283282602cdca1ce8778ffd29b3a63030fd3306eb5f802994c8e21384712d5f347878b227a4ac835e856becb2cd58c46a9475a4aeec0c3fe6dd423845e3b5881d6e9b0ac5193f17b05240b442924878d90e0b59ee1303d104736c6d1a23db0e711953b0e3fbb150fd27ca5741af7022b63e23d3f07bf324d767c429b72af2fb5f1049144a5e93f5029d01d4605b4667c0ea56516f8000e890c513b885b8c3cffe659ce46b8eaf64df126ba89a4ef5f805ea45271846ec7757af4a6539eefe9e9aff881de4f5c1a043a0b4d186ee74dddd6e2b853fd36713d800e02fa4cd91a95ff3d844fd16b47c47d5a741adcf9451eaa517c366a374a6a331de557c4561381e457a6cc97c35dcf7e51d121ea44e750462d8ffff2437bc4721b9077204269925e362f170b1a9ae0ffc7d2417a2801df15582855728124e6f76903083b2cec5ff446f9fa51b4d49d54a746b53d892221098acdbbed07426201fef538f81f5549f8c490e0d35abb293b1860ae790a3a3021e5062e84f4b7beea9f992022df286b47fd75bb4e9d6be029e0480fd8694269d2bd7b50e159e38cc02c52e591407c0521e94878565e51286f7037a38ee59fa4e3011510ccbefc3f705298258b638e33722cfe2383e25dd91426dda8a251adf5e88cb1e318a84f58a317c8cfaeb357ca1654675abbb0f4ff74df9438ad1bf939bcce973daf355f58c2ca19acff957becf86cadbc7ca4bdd59ede8966f60a49f984d8889af9bdd2123272b4d2ceed641d5490a9ca2d677d81c74b38d6e32172fe91e93bf5f6ccd10cd1ca15072ffa0442cd29f210a25f61d550cd2a1f9f8bedb88a53a4b5ad35ab4a7f1496178e11836b65e08aa88379f07bf6c3dd6b250e5ddd4ab3deb8a8d68c79a3e9bf034281f19afe0b4dfb06434de69c7cfa80653da4f7c371e5b851c0e8209fd5fdcfc8a8f67060ac4f434ec7836d6373a829643726191ce8ba240176d52be4f9ba3c97c40ca9a2aabe498da360ba8cf52ead65b3040c1c2bc14c203e3e02a8312470305df7c6329460510cde9e41f7dea35ae7bbe68a300b1ae2d260a4c24cbac5e0b9b4bbabd23b50d3b402a5bedd68bd7766d58da9b21da051edc9f69ac5ba0452ac651b3dfdfe99c9837dc67f00818b36f205f2f2f4c496ed5b6aea56bfaad62e645a1e45b8b199797541a702b79e0fba2d2353c0950af2be359c03d155ee2f8c77828d464d488d57afacf0f24af56e2bb49a3c5492e0910ac90fc74c0a9091856a209c472f258e728e8a5032c16d092e7d7378a3ac6e57195ff3df1d7a15365fc5419167c3b33031c7acec4a7eb6c5c97c0c6d88caf49fb82d8514564efb7cb1538fa086ae62b504a12f0ebd8950764a1c96f196eb0f3dcf7cef0c7bc0fd804097cc6faf566104fd5e1035105e09081aac9943db086b89a2d108735ceb7cfbe2e2cc276e25e53d90ef126c9b238dda394ca4cf2356abd53d6043a75cde74498cd5d191f42a40d8050ed62a7e9aff8cc6f28a75357354ebfeed9b6e25f9de973ec6dc740a9b9674a096ca487a6eb867ebbc3fad215c70558518cfd78c70f61fcd1407c91bba9f554830ceb8c825a72428d227324d543080b6cc94a1cdd15e17c58314f2dccf956ddcaca131c12c6e06dbfeba2966f6992581e9e09a2ba0915a73b6e27a57234639ac36a4330609994cf7ce064c3d2429fe0d1fd6006789d8515e2c1d377ad583958ec28ad0cb0296ce21ac4369816ee94c60762aeb0d7bd433242c43572e157aef73b6d3ab9c89b098f25896400ce61febf1bed56106c905f924dbd1ac00d6b5044b2a16a80b9d6713ca3cbb850be8437c765e3d006f7e36aea19c07591f251703c1be73c8b975bb4d652a1")) // 3072 bytes
            .contentType(ContentType::BINARY));
    for (auto& ev: events) {
        assertMore(ev.size(), COAP_BLOCK_SIZE); // Sanity check
        Particle.publish(ev);
    }
    unsigned timeout = 60000;
    bool allSent = false;
    auto t1 = millis();
    int error = 0;
    for (;;) {
        allSent = true;
        bool failed = false;
        for (auto& ev: events) {
            if (!ev.isSent()) {
                allSent = false;
                if (!ev.isSending()) {
                    error = ev.error();
                    failed = true;
                    break;
                }
            }
        }
        if (allSent || failed || millis() - t1 >= timeout) {
            break;
        }
        Particle.process();
    }
    if (!allSent) {
        out->printlnf("allSent=%d timeout=%d error=%d", allSent, millis() - t1 >= timeout, error);
    }
    assertTrue(allSent);
}

test(11_publish_at_max_rate) {
    // Platform limits
    const size_t blockSize = 1024;
    const unsigned maxBlocksInFlight = 32;

    // Test settings
    const unsigned totalBlocksToSend = 150;
    const unsigned maxBlocksPerEvent = 5;
    const unsigned heapUsageUpdateInterval = 100;
    const unsigned testTimeout = 90000;

    static_assert(totalBlocksToSend > maxBlocksInFlight);
    static_assert(maxBlocksPerEvent * blockSize <= CloudEvent::MAX_SIZE);
    assertTrue(CloudEvent::canPublish(maxBlocksInFlight * blockSize));
    assertFalse(CloudEvent::canPublish(maxBlocksInFlight * blockSize + 1));

    system_tick_t lastHeapUsageUpdateTime = 0;
    unsigned eventsInFlight = 0;
    unsigned maxEventsInFlight = 0;
    unsigned blocksInFlight = 0;
    unsigned blocksSent = 0;
    unsigned eventsSent = 0;
    unsigned dataSent = 0;
    unsigned freeHeapBefore = 0;
    unsigned minFreeHeap = 0;
    bool publishLimitReached = false;
    bool testFailed = false;

    auto onStatusChange = [&](const CloudEvent& ev) {
        if (ev.isSending()) {
            return true; // No error
        }
        if (!ev.isSent()) {
            return false;
        }
        unsigned blocks = eventSizeInBlocks(ev.size(), blockSize);
        if (blocks > blocksInFlight || !eventsInFlight) { // Sanity check
            return false;
        }
        blocksInFlight -= blocks;
        --eventsInFlight;
        return true;
    };

    freeHeapBefore = System.freeMemory();
    minFreeHeap = freeHeapBefore;
    auto t1 = millis();
    lastHeapUsageUpdateTime = t1;

    int error = 0;

    for (;;) {
        if (blocksInFlight >= maxBlocksInFlight) {
            assertFalse(CloudEvent::canPublish(0));
            publishLimitReached = true;
        } else if (blocksSent < totalBlocksToSend) {
            // Send one more event
            unsigned maxBlocks = std::min({ totalBlocksToSend - blocksSent, maxBlocksInFlight - blocksInFlight, maxBlocksPerEvent });
            size_t dataSize = rand() % (maxBlocks * blockSize + 1);
            assertTrue(CloudEvent::canPublish(dataSize));

            auto ev = CloudEvent().name("abc").contentType(ContentType::BINARY);
            assertTrue(writeRandomData(ev, dataSize));
            ev.onStatusChange([&](CloudEvent ev) {
                bool ok = onStatusChange(ev);
                if (!ok) {
                    error = ev.error();
                    testFailed = true;
                }
            });

            Particle.publish(ev);
            if (!ev.isSending()) {
                out->printlnf("event error=%d", ev.error());
                assertTrue(ev.isSending());
            }

            auto blocks = eventSizeInBlocks(dataSize, blockSize);
            blocksInFlight += blocks;
            assertLessOrEqual(blocksInFlight, maxBlocksInFlight); // Sanity check

            ++eventsInFlight;
            if (eventsInFlight > maxEventsInFlight) {
                maxEventsInFlight = eventsInFlight;
            }

            blocksSent += blocks;
            dataSent += dataSize;
            ++eventsSent;
        } else if (!blocksInFlight) {
            break; // Done
        }

        auto t = millis();
        assertTrue(t - t1 < testTimeout);
        if (t - lastHeapUsageUpdateTime >= heapUsageUpdateInterval) {
            auto freeHeap = System.freeMemory();
            if (freeHeap < minFreeHeap) {
                minFreeHeap = freeHeap;
            }
            lastHeapUsageUpdateTime = t;
        }

        delay(10);
        Particle.process(); // Make sure app events get pumped every loop iteration

        if (testFailed) {
            break;
        }
    }
    auto t2 = millis();

    if (testFailed) {
        out->printlnf("error=%d", error);
    }

    assertFalse(testFailed);

    assertTrue(CloudEvent::canPublish(maxBlocksInFlight * blockSize));
    // FIXME: This sometimes fails with threading enabled
    // assertTrue(publishLimitReached);

    delay(1000);
    auto freeHeapAfter = System.freeMemory();
    assertTrue(freeHeapAfter >= freeHeapBefore || freeHeapBefore - freeHeapAfter < 200);

    pushMailboxMsg(String::format("events=%u;max_flight=%u;total_data=%u;time=%u;heap_usage=%u", eventsSent, maxEventsInFlight,
            dataSent, (unsigned)(t2 - t1), freeHeapBefore - minFreeHeap), 5000 /* wait */);
}

test(12_init_receive_text_event) {
    events1.clear();
}

test(13_receive_text_event) {
    assertTrue(waitFor([]() {
        return !events1.isEmpty();
    }, 30000));
    CloudEvent ev = events1.takeFirst();
    assertTrue(events1.isEmpty());
    assertTrue(ev.name() == scopedEventName("abc1"));
    assertTrue(ev.dataString() == "zqVdHQVEJ79VrabxVlznH4HA40z7RcucYRmhj8xOZBvHK6Q4H7mXkMFw25PTn8sColBBPp0pnsr5lEKoYWLGdlsKXCEOXidIfrNF"); // 100 bytes
    assertTrue(ev.contentType() == ContentType::TEXT);
    assertTrue(checkStatus(ev, CloudEvent::NEW));
}

test(14_init_receive_binary_event_of_max_size) {
    events2.clear();
}

test(15_receive_binary_event_of_max_size) {
    assertTrue(waitFor([]() {
        return !events2.isEmpty();
    }, 60000));
    CloudEvent ev = events2.takeFirst();
    assertTrue(events2.isEmpty());
    assertTrue(ev.name() == scopedEventName("abc2"));
    assertTrue(ev.data() == Buffer::fromHex("f64c174ad94ce186c001ccc9eb56ea7a1292ddf25b754d5edd6832d702c6f6909a3254341ccd25fc1e1654bcfe0f1c1cef648c93f302cab73ecaae97a70ec1da3754e356865eaec94a45f3584c08a1769e6558290547c6a8b3cb9b6ebdad41ecf43f831f77e1ee2b4f5f9df9c8fdc977e8fa3673ef88f4e80e830f0a90bfc7e0e0fd4edd10ae8e938b07c334deef52132884d144ed996983d671d1d89ed59a6ee535cb1b981b68b0ac2ccfc53ea06b26b17a683ed4034743c2d1a22b3d2204570d4daab5ae3512b13573b71d9df8ee65e8fb58964bb6407ff167e26bdd428b1440ac6abb0c21694a0e6b1ca17c6564ffaab417f25e9cd409392e82d3e83f62d377afdd423d8d433771a7417535012515a67a5e86bfa2e7dc2fc7596006097d7a4ee6311583afcb012b7621aee051ee101fce020be992418ccc43fa4e5061d077403afb622929e04025f3e85b6bbab261d1d385f7200904b34a86037d2c2342e3bd2ac8ed536139b27446e1f46bbead27d96a3e8e797fdc1df1f4af377d9670fbfc022e9f806c167b1a376c6b0735992039a365d4616aafaf65e0d7082db26f2e4e2e55d7223164fe77a52c41a722e840d6d7a39eef83f3d6377943e56308dbb6914146c2e83951db0035c6d225f0f6ce39bb95b4362acb68d65ee6929dc0e6f5fcc4029c621fb0097aa33955cf4e2922d7451d3dccae3f19ade31c796bee71023b1daa23799451a934d1ddbb227ad1b4e232a15501131d40353fc55595d67fc8590830fc86bb755230f959973ccc8129a647a5110f5530c726a6d30cd50eca17a0a18abb0500f2e8db1cd6fbbb67747ff05ba49c6f53455fa3a188bd05d807867cb49036f1f764fba57913001194a31ea8e7e29b7d44835dc5bc799b78bf595267721f72d29814da0f324fb458246918b936c59c4b3c877e1f41381dee12f6d77e3dc641726fd969b5012e649555aafbbf1a4ea4dd45735fafd2ed8ba2100b9b1edfef5e64161431299db85711945f785ec454f2b06ab2b4bdeeea7121b211d20f68efc26adde7de2ce586c263ed70ac6784159be3d1742f38daac3779d1207332a5b54e36287408d5934e65b8f60618b3f2775abfd6947df7014b073467e66050cb222171e53603b24073995065963cd6246c39cd6a0b60efe4d20c77be4410d6638fff047dccb9cca51f2fc5c9d9fa389488cd9dd63b4a9817011eb8706fa5aac1f1eba828642a4f5ae2986417ae9473336d22dca772f3debf42a875da797593ae286098bec17bee4227f0e0735a2c2f1d35e290e043d5c64299beaf1207637834a84e3f0f650819d1091b8243627e71afca42f17439fac518d69dce29cceb640d24cd478921e5a4c30001b637888458cb3df25d8846eab7f1beed0c539b5ce891ba45d31a18d3a20048173bfe0254dd404f6d05fd576846b1d4fe53deeef324c78f840fc02143140767e1297c82832b99942e23a9dcfec4a2c6afd6e2ba4faacf1f2bf3ec491f35bb4eb70d33146b1935abaa6867711a1c6e66550375b714cdd7506dec01b184fe42f9b68f06bf1c4c36ef79d020cc8aad3fd17c1430462a1771598559b2f8902bdc4686f60c653200b619038ee536c76ecc958ae337942c6b300ab9110ee93da3abc1dea11cb6bf3f7f0290f281fa0f5deb8e8e4dd4a7730da36a716e693c41726e88212c8b864c15674162c3fa3e25fa618ecfa7af0bb4036cb20e4af8ae09a19515bc4e351d77f8309f048e720fac7607701a036ef14f597a2de5c686f0ed08907e53ead584cb946677355d3ae69256a4711890ce1fabff9b904e2c02033e0a9ce904ed466c076ebb485bfd8e60493504c6e31d02bf714be3abc665720b9ae0d7273cdc09ba90bd0dd622251de3ddb6324182f4e5725b1787a1669e5745fa6911a179de4fee11af295b33336bab0597f7d78e4bdc41081388aa0f5aa27cc0d3b6ae324defe159f13ae007565dfaf3ea27b244076a18053918931a4fc840bc507e54c66b7d992c9917e3bc031f88b8996a283e3f562c1499492b81a9d6bf4fdbdc12efc9d24d100dab085d752d1deae9402fde88fc96ea3c4d465aadea759cbb64cdd5d0edc1d907741b7e9dae6bc01c86edbdca95334f3188a51e522e50e79a991eb0f5b72589c017ab189a434111d507b8c91388a4fc5f0f7455e9a766feb754cddfec4525edfa46f6468e93dd269cace50f0e7c704a8f3bf9eb32741d019287a1653ced8964d56edef08a4e9b4fab4daced8662285cbcff0625c768c1602fe039be0404e2d83afd724c0e23f62809ab61c00473e07461d53266277e2659e89e4b353af0329bb1a5643edcf63ab034fff8f2d884b1ceaa96e38e1fe59234ab20781f1b5754a57a98bceac90797c7e7146b14fe31dea85cab8839e481ec2de899c89dbca4fc630054c2aa01884a682b2f6f453d7932bc264a159f6ec957b6c58bdb532c7f3cd1324578b1b170c7bf440f9db82f84c29b06e2c38c058c04738b1d816d431929be44082c655853ae2df5eeeae85c80bbaaa72c97c5a17383b97eec201fa7abc5b85d951f59dbbe9404ed524e9e5e399bf73451347ca875104fa197da395a6413e59960f8547c68773c3bf90c628009ab2a5f62ac3f24cc93c0c2e1c2dc831832322a3225bf7549c3b5b50233eca62fc1a7395d78500a01857cc18252a5df974d8f57a8b1dd5bcfa7643080e88caeb162e64e926395087198cc45cac81af19cecc337aca8e5a2b10576bb2fbb0e6eb60415c75b6b0ae5a8e5ebe830383efe23d7e04ff219f04e2930ac229e476173627af7f326892e1617b3fcd4fabcb967b42f387f10e90e26e28c16c0f03bd770584e8709f96309ab67eec8f1d430ed911a609b588454823e1cfa00c77dd938b21fbe9ffeb81fb5368c1d0f6ab0071ab735a5871f682d33f6ea00e8af50806ff913dcb58c3a056335530e1c9b36715d113292d61262c438b33191fba2d0cdf592a482d2ff16ed0913a940b5b28d54d0601b2c2454fac14d13b6a3a27a39a0e4966e05b1ea5ebd9d4b34bd62d9fc89c379cb5393fc2786d9d446c48bfa59de8e574a767bd8fd6ee43904d87d3a2940346e00fae3248e926fe8bf670d32e8e065c8930ca5e24a7609cbea1e9296efc70b430b223dad9af6c1b3d36949aab45104086521a522031f90ff7821f846845b407d28f32265437caedac64a3c1e7e1ed5a1dece0e749a16aea0818f362b65d364293f2b72cd21a69b8a899d756b7199c03e4630684466fd6476dafeea015df1c3b1a3f210ed739b940e037b8682236cd0cc8f803de4c307f7bac0c74bf5e775bf76d2ffd0e5532e77036982292ee58cf418a39d6e6f43ac13bdc37f37687ce0e0b4088799e3b7e5e0000790069364578cf760edbc253f3d5c25717d3199aaadc662c5519c0236531d524435f87e6f295c8c57dc85a55041cc9122d738b44b5b9bf366685743cc885065f4ea83dbe58e702e250180100b981b329ce9a099e7aae19b97a40f717a8986cd2d294d5bd578bc69d92a9829c83f9e78c38a637d785a53ec64034b11e1dc97e2c1480e417d6178884b96360af44fb2746c6afd95663454422b3533600403030b0338d94cff611ec76101dbd42455269daba8dc8fac818af0b3457652446c37f293f3af9e8a1b4b1b7968a3be9bb098c2ed32187a41d7f6fe013798a294bf30c576ceca10574cc2926107df9556201357b172be785a28b8f199fc2ca8622789ea1f23a9e631780462ffb6f6d32f877f268be981f95b180c7d8cb85b2f6303dbdf0259333016d018f037d9dfe37636a5a51001eff9a1229a0003b1b6891cbd2068c49f4ed8104178080bc1d4ef9ef18777200f3958e1be50dd698a4fdd80dbd4e91d6eb447a8be23b80ccf4bc90af03f2771956ea7691d3868d22bf53a19739a82cecae90057aa6894d9a7548cc7b54ecfb4e7729294ef65824124e86ac99d54deb9632299affd5789cf18517f69e15c4cb00fb3514e4ac51a41bf223a6c7a60b1cf44585caa6b5cec53042517690f512dbbb2a387f1c01a26bdefcbf2eca91977348ff5116a293fd99cdea52e32ae94b7f58bb70b819f03016d317c460eda99c6b4574f549dce30035479aad7c2a97aa2da4c12b29bc744f6a850941b2c234b9792e61511d44a6d56ff7fac9026c4c5cd704be540ae5af7382e9a879a64b0a3c29f0f0da90113ba505babc1ddccb811681f3ace9b6cc35d92771da42e776f4a42562696f2629da28302524a70c7443c06a545e3189953f6199ec65f99de83c2642f1d2a913d55561d82c6fed5d1fa8c980bec52f7c573057933207a47e8a9de3372a982dfa8bd7192a5eaaafa17186ba6078d0d2e16b6a347469552e2999583bab1c538931a58011c412615d3b423ca2020024a532bb062fbb3824060ee2b6b7c07e417e02db3e96b79d14a8e825cfbb2610a111d1ec27f83b978ea0b3033a0dee9af1d1487bbc220ee631482f48d711769f5768badda9af70fd1ef20b7207a994229dfb5a3da3d14a54abd63978756d79e8e379e2419da606d3062cc3b9bc64ff79df1d6fc238aa5550274cfc4fa2e6ded8435958a4094d75da42cdaf5966732a29cf2b44ecdd035741b0831d12356e386c5bfbd9c2f2cde62c1765ac9277ea08bd1afdb0abb917996f5bb85233776944e38516fd72fab5c12324db59527d31a97aeb0bf1304c963b2c995bbab48f43e4ad767ce7642ebdd4617f5aeff44053e1f1c7c2c531823d986c5caebc1041e3f7424b46b9de26ee28d78ab9317e7e91c73d544ee3a27d94e84589e86dc647302224174f279fa04479ffb4c69d21994bd9b7eee22fdb4d6a5ca90948ebd7d2c2ce9f457261da1d4e00cb5c6703c99b2f8ad55f730f32fb4672dbcfd65ab1028613835bf1e1c45dcb963929e298d3ba4d221b8bcc3e5ce6ce2f977289f50968e8fa3834e81682df058b517edbdd67717832674c1abe815206b250ecfd588ab6058e6008e5b277b94fa669380cb2c5b572124852e27675a64bde50301eac787d8c466b7a152f487cc366b04236b0fd15e9e11999412eb8a4cd0e92532ea44ccadf665df13ff50059505bb89ca89442374ad93bc2a8ce90cd1c71c275a06975464f974689f4bb1319125f7f15acf90cb7bef271aaef56fafbefbab751d04814d399b4b280e49876d46163a299a6074c55aa38ebc1a60aa5ac348dc89de9fae548716613f3787b20878f64a0fc08e7dd5b8261672d5474b2daecfbdc8003c47bfbba382bc26aa04abf6ea77e68726b09afeb73e3905ef77a0069daa03e1740137b338b2ee33e28cde8032ccea4a8e0674b50c2003ca20020f471606c8d8d583dd3802af00b4a3f02f432793a3c3115bd2d2c30fb9b1b0daf3c9bfc971aabd955d420d188a7c7e4b29aa6de14eafa996b53a809410dbac38c2c240699f8739000b01f92e0fcdfdb9913199ebda8b989ab989a08209c601ba0436ac7f305f76653061bfb6a837b44c8a15c5fde7e2001b38ddfea3779d7e77a05546cd501c51df7e8a6f3a0fd5bf79bda2a5c7b9f7fd34d413cdb4fa8390366e68ef59d70a974d9b4c82630e91b7ccaa92383ffbb4e548cfc9efc74b6e373a1dd704e7aa80030b8060dafe5d3a505cdd84db50137d25fc3d5792691e781dd0a396c542259afe221d1434168b1617fb2833332124c5aadd38f3c30fe2b6334e8ce9b1c38e2c9b013c038027652b3fbdf2ef397d74ed9588a03d5703ed47a1e919a6746077c2227b1236df90500fd366771383931289130a668b3ae0a9125d4930c3df72c8ed30de2d173256958ce8e5d3af80aec2539a5a54bcb15c50e1c935ca8cd9e259697f1c088df47e42391ec9a0915a94d208934c3f43e6349041c033a2c375daa51aef25d302269f522ee7ae7ccc63d7bde5c43133148d47f75a44303b42ad5f90e5b148490734164f2d72ea9b934817ef70c89d4dd1c0a2d9cf7f254009de127f23e4fa7aeb6e6b6dfe64509b80fa5568ea0b008f051f1feb49ecadf083d65295a9c78590d708a93a104bfe33a94a2569a598f5fa9b4244c16f16def28b18bda2b91c0b4990ae829b33cbff53f141917eda0480d4a47fa398e10e815fd0fee05231c4e7505514acf8e80a39f185f9e79274ce5e4e3f26cfd53d26580ec6fd8ee010af57b866de8c029e6f1fa576af4410940b1354c9e466b13347d7a082ef1f6813a3567c8eb48be4c7787b04b9f62bc92e632ad0966e4dff441c830e8ab82b50b2979f81f8d24753be3baefc3fcdbd5b53985ff3b096f2527ca55d082843737461a49dd95272a2c71e4c2e543e55c892129857f68c3e3586a612aa456fd5c5a3c33c2978b34c765f6cd73e196c74e271a560f68e7528efccc0fbd2c126b04395ecff3b6f833e59f3d1a68fed547e0d88448c4973a2ece8cb57d89dfe93a65275a40e6550a10c97148bba5cf8681b571b29dd6148cd31a5363051df8e16b2217d83789dc5b837275277fb6440671df27847ba1ee6d7a226721d67ff4b8290a43026907d65280780520d260981e61ad414ae5cfe93c77fe6a84784bb6334b85946bc1fc27b0466e128db6322777a9d2a796fe349ac62e8ba460868ecc149fb42337a877d2adb5d4be625b32b7c29cc767f55bf3fd68040eaaf205cd85e3c01a9ea2894ac8f1f575081c0a5d2261d95ad06cfb9486f852e7f5ff074e9d4edcc002bf3ea75566143599e60740ae6a3a295e5e35e528baa016266d2dcf0d9a0d6e94387e1b51810a33df9964f6f820d7027ee6fbb128bc86b46f58035fc65290ac69a76e914390dceda4f8693fc16d6c31302326e438f7bd604c9ebc4805bccb66e42052f81c9b56264a069e12bea6ae28714c68fe57834fa7f0a906506ee4ff88a6709f31843aa5db2657cbed8aa17355c265d95ed2975577db3c4a3395c9b1cc6a16f72be8a5be2856e829b5e8991a3e61cd823444e3bbfa86b74fe8364d93ef3e54168d17f5aa5f346d0fc709edb7ddfa8fe994c6a9d5c91396755b4c3f70b5c5034d2617ff09af3aeb669a8b39e3b55af9e130b1b4aa7299d8a59dab2d9ea2e6050516f731b114b50e399e16ad27932a6da2354415fbef6c68a43184f7984b301af477e9f2bfedef60e38d555a134cbee077c668cb3e8289877d854323508470f9e1097edeb67d7a13c69bfb6bbc589b5480d97b3905a788dca976b0a2a8c20fd799cfdf7c85120c04929d01371ec62e1a306e4bd06f784b4739d97f193d71071c67d89643e2b37f8a88449b5962c6e0c6455c89656e875d1da524a7eb47455f9d440a6a46ec3f0a07db3a5a190d45910175f05a7ca9c19e38ffad010a68cb291f319918c175b49ddbf4b89dbd12c09c430d8d1be245431806c617df134ece18ee42238d086fd614bd5967806d5fdc83519aeac79c135807c46efb19b8d59c0d34a324c33b9db142adfa404ef7c9ab1eff5284caeb30c097cdec7f6e3a0adb10036bebb8d38959d4bb9d56eaf1a43efb3c40d8bf746c7e23481fb1a80800ee2d6b9baf48ed306ef88c93730e1a2c2cc6f8b8f568d73a6fc544fa0e455ce0a8caecb376f49cb11affa3406715b1ab3f0036ef524a344239547801b58193f0b0f37e96206e96301a067754f7fe12ebc2f92e799c0d2906449d1f5caabd2c6e56a5646e94f2743aa4116c4c00d39983fe96d592284e738ef439137cbf5e84c00f4a52ada11bdf814c832af1665ad643e338a98dd93d8a20bf1cfd8e0b5c6b9424471c785955909b332c2590adb0dd7c970e74377c627648801b6c4090a03ae147a335412680c5969b2e02a56b38575a54d13242ed1dc23ad92a652ed50b9b271e7777d7ab614df89af6cd5afce73f0f18f8561fcd368b0c3d69aad4621f8fca395d852b3aded4bc76086cbf4041f91066817c76912912f1161bc81d1ba5272193466adefc0272c1b305cb9bbd0d95296e590aa54983c1eae48d0fec08029ec87d8290f825846c40dc4d7399f84eca759f3717b306611c027e7fde068844feb8ddd4d65e2bb326aad05cef3bbf37ee97313abf87c6e76beff6794be289de55d11e683ae747dd267649ffa3a751da3ea67775c31b2152c4c799ddb86af549e384a95b12553e21a74f47a5b2e80482a4cd12bc507aca90174c127f550a7680587505573cd853813b34c5b2e2abf3d391e1af56b7c0342d9284b72b5fba546165095428321e8b1a3fbac1901a25264533985fde1b2815ff8c3a91350884e4d31110d3e2ba7ba1d59c793b9ff57fe98040aa181cd6e9ae4f8f341fb1bb995eba7571219d6ddfacd8b5026cab6427caf0c4b25de0d0ded7340391c1df21cfe58dc1525d992855ea178c33455c7a55a64a0622932d01ef4fba66ce47632b1d0627eb481ccccae6be0ae46649f291b7a1eaa279dd53fe8ebd440538c165cb001fbc7e47f498a5cf0a9551cea69fc3c422994a6949b1e3a7fdfae382918d41355091d924d860fd827ec7abfb60eda0c51a7df87cd4c1223acf73389e6746695afc7511edec67a2ee78ccf57004c4d5e693fd0a198e726534e0197548ee313d61f928988192e14aadb1ed034efa29ae2f832fb442289ec5b5663fa56b7ce81fc5dca59f4b329d161c294651a4cc1281abcc8f2b3098e6e5d8fc294c84a204980bcdcf1b8b72fc4906da862be5610611a6d0e08b834cfea4f7d95a68dcabf8adabd82ab536eb12545839238df9f7e3a48160ac60ef91622a0db9d782bee6169cc57d0afe765bdd17ad236670d1a4a0d29013d570979de0eb3fc3598679aed3c19b51845590981222c3f445bd1ba6084695065d8205ebf59d998c0adac924616904c53e98d4398895035facd9897ae37f45c97828a3bb3c356542712e2df64cd3ac4cd9ee40bad28848001a8e6674887ab885852492ff138cf059c2d8740310d477f1979c66136a76f5eb837333d63166beccbd844504d1f02057c69fdb2569658e5fead8c3915a36f40194d006d1b603a51b29652d711bbc64f7723e39a0615b7c848b403c5999c1f0da6dddc77e1d053b24419d0dd23d606b3d00630c05bad82091d629d13e8efa48f3a2fab2471eb8d74a025601ae06eb5f0c7bfd91071e1d5789c73219e5bbea338910a0df2e8c16514dc0bc792e158c6552d94aeed519ee9913f37695c59632b51ffbed87754084af49b87392c436ee429dc193b5d60767444cefb8f7e5e0410621b9721e77ee19ca53f9929965d60f540b50da9543e330d79cc76a7382096b3aa64158e306525accbbefc76a6a870b8e5a9461448161ea840dd427a0e8571bb3d6c021981624faf31d821bd8d17ab13858f8632f89e90868b009bb6a3ea331380a81fb52827b1353a9d6631c87763c8870a7756c282fdfa852674f2231cfbb5bc662649647bef3a7422b7aaf794e8f3eccb45a7d0fc4dec805425d3bfb962290b03792f204ec6c187b0dfbb0d28ac2a6599b5c6130f70eefbfcada5fcda2a255b9cf89590c91e5cd28f6f4d08012c52003e99204cbe1b72d0a420aa32d3494c05ee45c3b70fb26ec7a5a36c0f26123593c520a595a688c433d16dda5c8b2247a84ca0960c7fb356dd48991b290f68163f70324b8f5a4592bceceb5c2f2b5f912b8f07e1d4a55bbdd5054ac86b1f11ee858862b4eccd6f1e071ad9095436910351adcd1ed50bba90f1be34333c1bac648a0762f0bdf2e56661f14567c0eaf22f27c6af11a344b45f60eaefc3073d5c17332ff359060a3cb000a8f3579e2ea778b13776aadc82daae182888369c1a664e51679c4e9d18a412a6c51c0a00242a036da1dfc1d67da7424635b69f0ca31022f843ce5697621ca4ff28bac5e3322edf91501772b4aec582c48ddfc4cc373a6c881164e0f32abe7969e2753f72ba15619292275ed5b8fd56820098ebfba78301f9a14f2ffc15d49a5281a03c20cac1597c03a2fc1fd4be1f64c81edfb37472d2584e1b4f3ca70b0b6b9abe77e2c7cd2e1d287a1318ebd0f5afc48f6a7ab14f791a02482dd6f442034938bf7f94c473a252edd28fd1bcbc27d0d298b4b0ed3437b7ac3d5a335fd01ad6242d71be251b9c2de209120f83907430f578df3302d20191b77f343682b05b562abe18b97676caf329f686782a49a24a59a22883d3594105163dc352320876132ecf38caefe0d6e2c5bd48e8dedb63fd2d2ad366974c688d7b3ab47219989cffcf7690a87a8577a02640ab915273c239f28316c62989920b1afcd061b1a99fadff687c631e4f19a8582cde5640c3235d5a4cc8824a4035e5ab37af9174c1e3af71036b961b2e903bf213da32698a5699e1864a646493b7b95909680c78c2f47bcdfd1d6c25e96662a25d47e5bbed99897525d58e17b59158490362347c6746defa9ffe00a4ee4907926f018e6eee4e9c1c314339e48182eb2914dbef7f2c80568823c0bf7c110038799bab4a76c1975e0ce95a24d8548e9c44d9eaee536c1ae583d7916ca3c0a47b53a918aaa4cc36648d6cee14a8453e5dea937e630945be736270123a4ae2877df76e3685edefa4cfa555a488d7650c7054f00da8516c9885a963d264ad584f2d20f22fa5308991e92557db5b2c6ed4683eb0119988d83061c3f90bef326ed5a30b5d1086b0e1a32d6896115ad60b0277f32f68c7de023796707ccfc91797da8e35b07c1d57046176e8147a6641da4aeaea466f3a40a4f2c6be96279fa247f9f9343cd31e21c5ed14db8b413f6c9819b811b844a757bb9d48c4dd96d1206af5d5e5ad8f0d47428871598c3195e9a2443f3017034543ee121b7d3f1d9388ae143fa8688836e8452a8eae14ac9a9e9d1518ea4414185e11e87a9c9735c8888f83743cead233f8bc2945a2b33c231bdf9d52a90af02617690d4719e241bc587784b02b43fe6d5fd5f8790f5b969d9331ae6a1fb47af217f6c02a525c4a11961e7a6cc80c3374362d6280d0b2ea831b74f20a1106cd94ad301de46d0764741265ba886c636db6ad66ab1237763e9f8ad35f5260047e32ab37e691a5783610997f2fe2df793f04a22991b15870eac4643ab850abfd32000cb2cf775e22d4a8e894876b876de497fe45a98686bf20c861859d2275ee9ddc41e5b9c360fd187b9bef468bc7ae2e834cf934ed1f5adab414ef600426d5dc402a1459701b8bbab664dafac21d1cad1be78c03b7497542aac1bc3c4053dd944310a8783ed62271543e3838c96ae259704276879a73bb006ad6c6093440609c59aef65f36820afedb45a6cc91635a5bde6ddbb148cfab6e4c661bcd4cc5d687b2427186c9b4a8374b5f3d1b4b18554ff524ac02c82deef3fb69f477024f1cfcafd23ebc6ebb64eef91129f5b30ec78212c052573d880e9acc8e59b77b31e9344ec101f44f80687febaf589f6d424e3845ca057ef4f72eae4a67f6c2ebcb86383f99b028bb52211f3c3d56be4667e16e155f0a98b2eb2e844a41406a116ddb701c290869d6ede78050bc2503eb35d4722d86139084a8991758764429bdd01043cd0ec73e3782e4e989ed23aecefd0421b68d8cb1016ac8e69d2c6ba904e3c0b07eb8baca034d6566b00f892eae404eee3ca7f72bdcded0725cae967027e0987e6b134a31b689a5fcc7f88ba52720265fb466842e40fee14087c4c393ee8e92f587ac5a3da6bbd45920197981f77858c5e1c7443b18fdbb697b9ce22704d3bb50f4770b277e0c9d43582105c1d437a1290797075970e590544296a6a2ed1ae8a3f336c7aea4e6634bc3fb5ededbb7e85f0e03e26642393ea4e13520940dcbc2462e909635c0794ac36362a8d4e72317ae6a04cd07e61bd0a9c9917471e3d507321738877e87e96fb74849e65bf9deb76987323c5689ae57c04b25ecf84727960003c07834978ee6a61a3ce3bd831c5fd5e7a9ea6ac74903a0e61cdfa70ffb29450675cc414274cd1f37f1bb572e212c3e83935a251c331fa76df12348cd3ef9bfd0ae58a17315f4b65b7d2a0d44c15026390a246f79e0951925d2c537ef9ce4b53bebd3924495437d0736c371568d95293d8838dc60c935f499a83b29ecc72b028668301898fa69ccc896223fb43939824febc055344a752b91087235aef5d55121bd0006263b49d6ddb1d26c8c7a4fcaf4c4d1307586991e51295849da49840040b4e466f8d123f69ef715b1a33a768632ccb78e2392c5a2500a0c958521efc758838f0aa72fd6e9f4ee04fd412db20f43beff1995f771a982e9ac23793a112e19dbb3374bae5de6cc6d4de2c1f00cb5116d2e9f0c0e4e15b2e71287a530bef223731f5e47d27a993feb50808077d980fbee38469853006286bfa814fb53a0974a6e38bab60051734e6a0fd5d346dd57b1b8e56994579548af82ee4a62a47b5d28fa3260668e845b661ceb1ffd46db0f673ef19dba0842120e7fa118497f2ae31db699261392a9a84cced2ef357192bedbe2b378795b1bf442d5e6d73c43bf8d6f0f2892d1c6b25dc5b48c9cc3014da0a6791fa0c006e2141bb8bf4845174ff6f3573df627212bcbac108bd5d44fe0bdade33f42c77017427afaed34c9b87c3a8e28185eb6afdd895695fc4bbdaa803b6ad840464e465a41ef5ddabffd76510940627f2fc0410c7b30f434aba6c63c3ef56bb0b8f612ffac9127db6c02261254b4993c425e84108d81359ad3b325645c51edcc9082c33cdd5dd4ee2c7062db63ab25eb4c31f0a3b0650f154829c5c49983f1e4a24a31683220bdf80ccc4072c35762f189d6841f301e38a905432afdcad7a4688c60f9186bace97fce669e7bd2aa03db8a6f82962f1e9a3077a0f759758edc9ce16aba12e6e4efc2b88c3fca75c3118d98a1c22f4450bd52c5f0ab1e0d5e8f6611183256a164b26887f2e2219d0d9bb2d8a8f8958f335f4eb654ff8835bed607da1ea99622920ff6f5ad36452377b26392f4b1f5d00b08c6f5656447ef59b53411b4bfe3267a6f306fb6b29e8d84a7267bbea1ae7aa4d399daed8d8abaab672e242f9df2418bafc14454a9d0a9477d6b337c43cef171a6e6a14b6fedea8fb73b2ac25170705d7a82958c7a4dcb19d4526aa4852b2b759b9e8a6f6a51a8c019e5a638fd9b2856ead1f108c3b5024a2d2117f538c4584dd2e3f52fb16bea9019d88e9ca6ee34d7f84874a05db31c0de17d958684e426a65b4655776947be8469a145578275c88190c3d3fca09881da6f18b8ea6e72d2175eec5937bfabe9d1d0028911cb70ce42d157d866c0b200bfa70c3542fe66b8fa5f5ae810c51c2eea5721ed3bb809d181fd0d49c1e765404edc91dbc46c3b4ce0b9e9c05312887eecc6fe576948b4618aac62846c04ff5239268af23d1527bd05e7984244a19b524599e5c1845de09ef4108a0ad3d3b24318388252e821ec7bb5fba137252e30d8d0dd4e5750ee7343b86ea467ee77813bcd2906ac3941dabfb468afbc13d9c87ddaf85ddd3a0c6049ae46215d29336782a496ce9b7767468cb41e764e0f8745a8ebb5bd6df0d2866b3d77f15c405c7399214d7264c02777e8c06390b807089d352ffa53b327c3d82d4b7a592e397e80edfc1002059fdb7b7c97d2e02308fe744c9aedfcd19e61c7e820c508812fd2e9b4a0aab399d02dfd8c04e061af5a7fa547f804f1d63f27de460014f17a2d76ad5fc358389bd05615bab0e9ff7c93e5010b17d2ff386fdfab88d8ec40fd0e770a030ea80964a2caa3e2bd366fd7ea068ec395170f7e4520b9c73d7c56085e6cbf92e6a0146736e884d0ce203ad2a7f0b1ef1b4ca247b5b7fbd1cd28075663c261fd50c7178cdad665253b5b6a98672cf7cc5114ec262ae72129cae46ad6b323e72f3ce61faf3d40915e2b706cd2fa34c2ff6c2638296aa4b071dc1a2b6930bb78a814bcb4b28a123108faee2397596d94bc33ec63779cd05681179fd61c0b46db3ae0f4f9038a64aef7a7ff2c4def8b7a06c758e4ad485dc4561aca8d0dc9ddf33ea9b346b7d4e8eaf3d090cfe1d0e4ff8e11c62c85c50de31b47f6f9c691e47ca941ecb202b8bbbf580c5b85cad1821fa7a2f5f13d4f3f8a9af3a042afa2f3f188597934adec79018bfd831f6cf00d66aad00ad497b88baa314deb0be2d8064068959870eefbaca3e295d86f6e76094ea98ead89d5c22ea2a896122b58e58e3035c245b70e9ececcd4ff34dce90e382445289e07702683878062586a90664ceeba48159d1b25dd8a77fe21c6abeb2b1d2ee616029d747de4756efde52099b2656f3942ffba7a99d5b1830abb1a4ede8fe87af6d77d1e937c474c9435583d9293ac4e2928578ee593df66a778f243637165ed4eaee689670ea758472b0c55de8f75bce9279ab38d44622855f0d96da2c7547744ab7c957842cb85b98e972c86105026da5c11ac47f588bd006650075b3ff0ca0a8063efb3a8ea3295a0dc6428a354f5d93abb7c778dcd6c08c3fb548f39f4caed9f7f991726979d965056d63a450e888b41da85b1afeef584fc18883a975db48be9cd542fa68b4c97c446e6b5d60fda1a903a77f6e9fbafe62b576b8e4ed6ac89043057e12a500768c51d46add18610e394f421388b79dd5d9299d5ff4ac06d7632ae42561c76385e01d4c16626e99add4dacdd3cda72ba1b09dc3847afbd7582c3fc9e62aeae53ed028558eb558574086ab12ff26c4d1eafab1834b646950dba43aa7e32d7ad46bd8996afa6c3e017eea1bd83321de1e7ee14e09d464836dd71bb3615d464a21ee2708441a3a7737fee5c7a5130173130de0fd802603a3a3f694e4a7bf458f67b0f5580ed1db5092c7e118c93e55b1438956036230fea3eaac7f0ece4b79588368defafc45a50e9dd63c82fbd9022b7078978489cf0a7e7fa7830a8bd6de8dbd99e5e22b607b1d9f270f8b3d1025666c3467372d3c9c6711e1897f7deca85dc38ed09cd681d12dc08540d0c34128bae4b750b024ecaeec89dee24a14fa04d2efc0c2b3485b298c0be163159eef25c654d7b98f735970b36024601bde371f88684060eec0bcd3ff381b78e3048d1662646806203bdacb044766fc8a04aa7a9608d98a4cb5f07ab48c84297dcb492a992141ca880d839d644b6feb7273ece1763dc0ee49860f5d78ea01704c95c9900357f78931ede5976fda40901d2d7b5f57f3022c4205c584ad0a1add5d3ce7a67873543582f52f9bf1b6de242ea179195cd4bae701ecfeb9e57cfe6fcede243a002bc37a4ba8f29c2db5892a6207b4b70d3be3aa2bc7ae87db057313f4b7dbd7cc30e80d1530c47aa9765d9412bb9bcb83167e2f5eb04cd666f20258bbc83777e7e131675a89fc0314900c7718fb6168f24ef669f0fc4c783663340ee88f9fc900dd3e27ddec01d007065a39e479042ba5335eff262d7647e47f5cb969fe74d6733104d0177e2e8ff562ee73be7390c61926cdb56e02d4bdaf6165ee65a34e1fb55439372d92305128703733ac08b54a695d3296957dbc6c732b4b9a6d78e7d1749459602554ae0af21336a5c4010c9d7659db1b0a424956ab95d5f6cfc6753b1252ab2f45a089dd8188468690dce1eea4f2c7a0cc197c8a75a70a51566a3078f33f5427e5924948f5b8ae4a87e97f334fa859176414a63f0b7ad6487aa3dfbc86abb85fb9080433124e39bcecbfb4cb6cae5452b62f71097d27c489a9bae93ef34a96c390e206d4b3baf804a98b8c9d69b12aa66238a47d7cc594fef60f639b899b1d36e53e43e56d494bc284a67216442938ed3c4ee272b3482c91fcc4d42419e46e7aa43e32f01151b86594dc985c74b7ce15d996d0682bb67cd8044a4e409aa28f1e5d5091b524c25c6f1f6844101be1a04c920998338e3120a05d40adf987a99e3f637847b80ba3241f969f093f3c04fbf03547f4af7529e0bf8078afc4d06621ae612f6740a8b685c6bd96b16f76df1a4bfd25cb56b362ac67ae2471ea4b15e0d415c2406f51d5749289c35a06931e673c6497dea06267e333308a8157f04503082d59c4757c605f168fd6a959995846335284d447952ee1d2f4daa80be9a5004bba002f0e525a432d03adba7ebec669fda9c7288b6fb9b5f72a5f8fd5c7bfcfd7ccf0fafb518cb15c4c3e8a0e7a4a33e8a6ad2f208933a0a6b065e18570312bc355b4244ef14193f2b0f86c3a6250157f5f8b8a5a13edb4c172a5783a410f9c9b7961094dcbbf9411527cf36bb238acb440ca59c7afc6e41ea0be7837d4fe01dcb9108ed19843c5ee3431fb53e00c041e3db0d1ca1139889190f0411395e5df8471546cc013fc82f0eaf54c32acb0b3535d83d939cedfda3f4426e3dd853c122fd121e8536859bb29789e9d99b9540d58f5434df5883e6df8729be0001f28a6cfac384b524c8a2abeb51e852bb6e548576921c371a3ee79db515b4243d03d23044b25b5d678bae58b3850619a97ac0d3dcf875d7bbb6d5343e8929c2f798f9d1963c19e37f0751743bfc751f7cf9656bae67b9ec75d9920d017539154eb8a96c3878ecc4a1e82a11225fddbc1236bc8abc5a064c9f8eeab0315eff59ce7c97d6b4648c413da2508142634d18084cc9caf64c3ebd9fac7e9a94c8b865b1c3309f8c21acbfa0e2a9bfd3ad2ea898e7f68c36ce6891710c66c7f26622225a13c1ec26ab6ad096990c06a3f84dcec85efc69b500b2c96f0dd449bc19518b932663873ced3100d730b9d74d0761298f113652abd0597501c59d9b2063705c2cde90ee20b3019d4811e4a966041c0b8a8e1a82078a74ad8ff7cbab359d969bb88d69f9d681520aebc87daf6f99e0f65e0fb74f95aaa8500afccf58375dc9f5c40ae1e268525a08728607f2648ca8fb82d07e98528bdd5ef291a93a532aeefbc7d50a0b8f2070c4c5c17efe5ab75edf85ede38837fac579d581189dddd664433b06bd55ed8b52a7e063f4d48d4387eb9cb3b93b2da389501b3c05ccbf29cfd2770a700c203a6128517c4ba27025b877c623ebc940832bb565a339af0a13b3ebde2414607e77390372ae65820bc4e4312c35c9b25732e12343e48b97909a9ee3b4867ffbdda96784412646cd2b21c3043b5926bc44b9c6479c48707cc1f52bb88aa62eba3ba104f49f92a27f7de1b3681245072cb6c0896af7a6f15e5ee3189ac180cb7f12ffb9b3de96ac6577b6d6b41b714eccd4464f4aec7b1fd7d6f67c4171692490a6f0fdf5b81e6b2c5cc408b9c5e4ce81fc3891829647e579a4131a6fed1c5614cc2a8afce34ee539dee182f6bd04eeebb85368b6d1b563529f86bd53a512247a7fbbad761f2aec9e2e5002bdce5902e13ef1a8f146abf1b415bbba257158726d95882d7e6826c71b7a206dc17f687079271ed3af9ccf209310e24f64596aae57c61ae8866c9c3b0f7f4424d276d07b0f0e7d92498aa852e692a4dff021960e35f1e05ff67fab897f8ee1dfd13b4fcba6d60c8fd111dfa00b4cdfdfbcbfa4c8dc862b8d436c6e1064550bfd5c28d8ed3a5b9cf17d13f71ad866e3e2a5b8903ee3d705e6594b8111db8a7845531462872f233ebf7f9490f838e889109d05977a3101c96aea9edbe8b518e5b3bcc94f72e01dee7b129bed0805ee2cb917c96517a035421c6d6a616ff2fa675de1147a813d0af9ab6db41acfe11c5fd45c77dc7ba73a35b58f6e34911b0443a96c5dfd8bfcfdff3a811ece3c91733faf1a1dfbc21da79431aaca4d5ba7d454b44f318821539078c854cbc305987e45dc64dc6dac2c93dbadc8b592101ef873a6bd3b0c4b318165ffb94fa2df6e22f6c7421c3a47d76d477543e792c829e6dcf825c221ce19d871fe8cc49578fe9355f6c63933b9d3af0e6843b8975de3b9240178ad675ab3f0376d1ac6c0ad164fe52a96552ddbaafe29c2cdd914391f313b95894d1c05d474c98172ba357829e63773c3d355236c6beedfb4b656e702791e2dfd725aa1cc1e9e007d260e359534b959568b5ce24bd1e5e4fb7875a803fb0d9f40b00567e59a6485d85050815085d73ae64d97a29572fe3045760306e8be49086d8b1431d330d8a4bf78000653bff3e5188f596c93a9ef6aa7732a6dffce84065fd77d1a85e4e4d1c0f66078fe05acf0a24c1acc3c086f5991cf3b5d5d31b026bdee055a5a3ce2ae1a218c35e5ef9843dc08ea4541eccbd1832cd9f907746a314c4da83e27802d1690c6eee620e1aaaed6e15968cacee7bf63f82ad1c65bca60c9d63d560963bc577f2c55c7c1df02a8d228793013a2f8a1bc7670d305a6f8eda3cc1db15ceb0ae0906200b586deedf4d6687a675cb55a1f719ee1701d02a923c09d4ba1cd32813c45b755bd3576ed742e2bda60016e177985e1bf44bdd6ddf861f4bf99dae812ca330e1b5ec8a15514a9f7150a7e5a4c074314ea4b164488f61802671798f2630014904c174cfcb2104d2481c30d966a46310526708e7ea413d62eedd033ec948c7da7858436ad9b8e4eac2681b27864831ea5a8365dbfc643af6b83777094ca225c171425c80d52904fef5019438b656f3e6a74b139ed494ae9e9be49f5ec072f5cbebad1e9289c801fd654788dd34deb4f0a1c60d2a88fb4fdead7fb12d42214cc97919df7b7c83dcaa326e5b68312401c70728d76cd2463069c19eb2933b0e1dbeddc95a69c8063bdbee2cd34f3fe48ea642f0ef9810d8e6030da1277d84373e3031e8ee2380f0a1b2d78916cc68988fee6d2add63f389dc8d7c55c29c25690638658c85d4a0adc9ddbf0c290fa04d4766a0313c68c42e9986ebf115ce27f4754045d465d104591e35c17128fccf29bf9089e0b1f1f6a0491e303eecb16b358cbd72f96c6048f4f9f852914fac1355dd98276ddd457cf3693f874647595647a444afeff7c8c5c268ab2224c397a0ad47bec5454220eadbe16c4bd0e57241b1a968365d7045bd02143a8c353ca0e38f9dcf98d3eb1042cf9bae4ed781705d79b74d2f7ca82fccc8aab92ea5335b24c00fc48d47d87bb98be179e7496442f5ed2902986be617f9f3817bbb274942adc3c5357ca60e3c15ccef964107a8483101aac85a7ebf16ca7d51f80d8b3c35f64e5937d85145bc3f53d717100ed929e6cc37256667c7a3b8869c0510f08ac28c39e119948206c91e48bfa8eab1190c355e09504f03ad782bb130a93d701c1743a3b1cb38eea1d8c13f3654f57a167e9a32af8775481bccb155e5ac5f81ce4a5ed5b72746314c897ebeda0231f42d96854c0204c2e043f98f4b6766abf65acc707d5f6895f09f17e524e7dd8612e66f1e5660229f218e8544df89fa36e47d2c92ca29f68cae6ca273e7bf7431e3ef514602e8b91bb4fc57f5a99f6888ae9d47026b7c1f5b6b2b45dd18d0e6924354a852c274b1484b6ca1960e6a6d62afefcbbe96e6d3031b3020bfacd66374a3e41421b7794591908752e8fb93b8115f59d70d01e370eb920d48db73b20aed9c404873ad9c44707ec0790024071d2ee6d7c5085830eac2cf6e5b56bef485f3944e1d6498005c3edb0a3e72200bea9a5c44d8bc84d56941d2165cb1c6103f0e7d4c94e60141e5fbf99bec7e05ec07c0bb176e2a199db65227ce3e353fab31bb9494e5d626ed2e4b92d5ed5a7d31e0dcf5b8494d2d189a95c572d18eb6d9fcbb4ef99d493d754ffa2cb48732a520d488c41db4b9dfdf2af48c5b491acbd7f39ea160af51d58d515edf8532e7b0c5412106ed3a21ca53f33fe45bd5f2af8939859adf6bbec80cd85314b450beadac6e6068158028625f3e97ce8afc2986b1d8c66ec44ee3cd2a7f3ea97c1b3722a6d4c407673091ffb366112fc9e4546c13473414ab8fad9c77d5396fc36535bb93bd480f0f83026b62a9416cf4a2c753ccf27b21c888bcddd10c465a262b2bad911a1d6f49815dde4e89edf14af90ffefc7cfa7fb57af91ec0733fdf07749db8c2944d08757fb387bd1d48c112c702a8ddec9212afbc1413ff2e2b477f5e9b62a89317dcf8edbdb7abe2f1fe75c254895ce8a11a83a6ade6c0dafa36224ee26d0e4258cd41f72f5c546bc6d39a8961bfa6be13d1db531c178161a552c78efffb26d036cc012c1c036db33ebd03e6421228400141e0aed243b2d329041ff52116f8b36cb80e9436f288c31c6ae158bb2a6f06ce75e0c0c408a44160131f3235940b94b5d75d112b64285c4d3ee1e3161e31cc84247fe2e093c07839d0b0a084457446671b39bd66173444c4cbfd2bd7efea83a54ac75a32f2c4c6566aad6b8e9fef98becc166bda346c8811aaf306d3cbe3ec3290a02cd311ed7699e1f43bd7b7bff1d77d2c26efdd9f8194f62f14d9de564035bfd96c537a41d7a199725d901568a017a78af95b4d399d4ce12891a3648db00917deff71a390c996bf5242324f51b295dd6cd0f88c2d6f74a342f43261df3c313f7c894dab4cf539655224c510440e708ce349e72f33a6324a6ae90189c39af7bd9a99564b59206826f7b9e9b6a550c3469e7ce03c33e5a1d366d846e0965d0d05ce00da6ed5b96e83bf5bed8459450949bbf41586add37d0520ab249c45ff046df20497a74637cb537825160870cefde19bfe8c7162e2f2b1b2e1efd23b620f56da81c76490465b65da5c870973ea5ed8323ccd3250db70766cff56ad8b720fe2d6f534347958200030eb837aa14f1dd4b24388898e749e02dea16fc57b990844326af1caee72ee231611fda6259914cace23405e013387f30fdba6cdd04e8323c5139c9df5e49a11faadfeea7ff7aa7aa98da3940507e71ad7a75aedcacb714dc0e90d9d27944ffd1818427a10117653681b70c754a74a1d49c8c773244b4015b5a15c9bf6cb2d7c04b87be6531af07ab02abca6406bd0e087f439f801ece8a9042dad9b6591b1389c99b9fbec05f35c510489f7824aa243a9db1516b6129794bd982ce054b08b403979f781e6edbfe6983733e57689b2421357c4a4b2a59ea06d89161cbd7f2250441ab51b0de825fbc17f3250b23a91d1868bbaeb2c1a6276faf03e1a6eae7a712da61308f5e4abd4b4e1a5086be300e2d7f04e93e5438b2ccb6b51e6a13ccfc14df02b68cafdb4a802f1ef57ecd7134f906cd75d450ea0b3a7e6cd43fd39b4f0da5e5b7ab8252267b86ef570408fd501295a6ea562f3f3e2ac8966469fe8ff4c4e099a02cb871d1c1ecec888bd12b2066a9643a06129283e12429787bb14a594ccd8bb3c7b8f48a5c66f6d7212e7f0ab8fb441e408842c78e05d5466eb8b297ebd91b926ccff4953c6fac79cf6d6fd1ebf6c50efc7a3e87262a473199509ecd2fcf97de13375290413227844c64e8b47f359df11d26dbf80080ce72418a38046eacf6aaeace7fdaf5e442f3b880dfb19da0958c6552d2515e3999291b52f0850809691b418315534b291a8f801ed94f8b80d1211904c4b967456cc20ed117d10db91a594545f8a140617161c8ab62a7bfa445dc02822b63f0484d089b9908378bbf811d368ba5d090c89d9d91051b42bfb846a5094d069d96f3c2975dc0371c18d633fa89813a7310a50596a803b1010947b5637834841e43b92f3c8f0ea373ba481fa23da54b6f69e331265a6c3ba269cbbdd4ac79e27de1db6f5ce9fdf3f4f05e6c22215faa6282cc1798435b0b56d0958578b58b7809783d4f4af0647ccd2ef445a4064533f11a2479c788b9c12466f41d8050a834ade32bbaecd942cf1a2ae162d3b3533366d920505dc690cb18d9684f6e07291cfe0e42efaceeaefee63d63bbf09e85ebdc9331c1dddaae6b1e7294e19ab9042625ee93aad302cf8a26db92e540a39eafc9383f3c67218204cfb97a928236b04f49588e3504d4c76c17572a116b8ae04135ebea3af0bc58ce31fba8cbb77ad932a8d5394e1f9ca9caa7c796631983f95e17a541b212a710adf35908dd07042467e620dc0a72fe31ea4cba8f6e4d46b7a22861fcef1b08ecd850811396044a4cb31b5fb153bcdbcdfa72557e5e8b8d333e71bbfbb3970bc3d878a75845d762e1fdcb7405d3a8dd63215d1c48f1bea78ec75073679833e50ad356fa18591bf973b0759fc997815f311bc8bc2377376fefb38cf27b963cc7bad0d2c6bd0b93b6801cf7134801c366e928b58675a48c2bed0b7b35295dabf25d0d4cb805b38914623ddda9f774a594d272ca2ba57394985f52e0e6ada068c34757ae7add7042eb595122ca595157a1532bafb8a5f04ef02605dd31b00b1e41f1bf58d90fe5a191f2984c58249bb062930ecb3c0b8781b88087facfef6e98a6c9090de7ac670c9523285cca83684a7547b66618082457457d59feb64624d897dfc19f3f327aa59b6b3d130930f07cb14c80285f1d18216f61b5a0a97a80ca78c375925e788c3943ab917a7cae664d587dac485a0ce0cd632f5f2ed021384ca7cf4a672d45099f532b83f23be99ce8e209dbdc927e9b6dfcd1650917aaeebfd61575e47cdb62b62a154adc4aa4200ff436515038cf046d16806626bcac99e75849a459004b1eb66f7fd1dbc191bdb2abfbe6d766a3327ab0e5d0b866ab890b080402252f39ba292632fc9c99c2fa14616133eccde2ff9c43163d3f2e1bfbe7b6618f5088909bafcdf5b27a4036452cc4f89ee341732250ec9c9eac750f7805bf9ec84d40e54163a59a6c9d8b4194a4185536e6625f6cea8fa9ad57357364fe0d7f0d93b830e5dde813ed8a8cd2248e176b37be1a506a32570d53f52260093421e4dcee4bf25ee0d3bc5ff7d0ec64decc79957c043b5b7d79539701919f18d244de02fd4550db2c78533c14c4a8d663225c670aa61e2912780b1f2df4035486f3de254a648dfaffcd884b281d94b6d3f1a065c161f3e9642c10e9763470c7709908b3b96cd1cdc0e33ce4f085c6022d2939dab87aceb4073f5bd2a7bb5508492d74e58a9dd9ae6c0b683fbb19674e6dbdc7ce8740b54287816a6e17f970448cb99a75a20b1665f817b9d25b5c34711d5639724e45c2c92de4e91fe1ccd99b7bcdab8eb421080a40210c3271ab7f35ef52b94e59244d0926a2bd6c98575c8adf305443f8387a7ad7d33c927adbb89ab8cda09cf144ff963af01096cab65fed82ab058a4c8696e6302a2c1d94e328a923a9caf3ccaa3b223d5c88838c4ea697fe30a6707d6bd05e103a23c83a1b00edc67e1e96c6e447623d2f715479ca110d033c9cde2cf4ba05454bebdcbe76699542c67a9e2d99661f34fbfed55687f177769651eb0ed63b3535c313249c8c2516e57ed8d78308dc7c8876a35e4ce08f7b6a77fd0bcd55560a0eaba7497d64918a3bd0d17f656855865476a3b90bdc4e8d2e")); // 16384 bytes
    assertEqual(ev.size(), CloudEvent::MAX_SIZE);
    assertTrue(ev.contentType() == ContentType::BINARY);
    assertTrue(checkStatus(ev, CloudEvent::NEW));
}

test(16_init_receive_structured_event) {
    structEvents.clear();
}

test(17_receive_structured_event) {
    assertTrue(waitFor([]() {
        return !structEvents.isEmpty();
    }, 30000));
    CloudEvent ev = structEvents.takeFirst();
    assertTrue(structEvents.isEmpty());
    assertTrue(ev.name() == scopedEventName("abc3"));
    assertTrue((ev.dataStructured() == VariantMap{ { "d", 4 }, { "e", 5 }, { "f", 6 } }));
    assertTrue(ev.contentType() == ContentType::STRUCTURED);
    assertTrue(checkStatus(ev, CloudEvent::NEW));
}

test(18_init_receive_multiple_large_events) {
    events1.clear();
    events2.clear();
}

test(19_receive_multiple_large_events) {
    assertTrue(waitFor([]() {
        return !events1.isEmpty() && !events2.isEmpty();
    }, 60000));
    CloudEvent ev1 = events1.takeFirst();
    CloudEvent ev2 = events2.takeFirst();
    assertTrue(events1.isEmpty() && events2.isEmpty());
    // Event 1
    assertTrue(ev1.name() == scopedEventName("abc1"));
    assertTrue(ev1.data() == Buffer::fromHex("477ff096fdb0fa41c63c791c0436020606262e6746087680ace7103ba0828a7a38395fe4a6556c997f11d928e2498d0b69c47f09fd943be483e282acff679c164e124952a62e94e45d1299bb75c78c1e531143c134e6731c8cf812bba2ed7e13259520f991d82ede7ee7c41a5540249d36dc9a4fd501e5512ca4ee9f0b5af6b1b60a64c95545db2e0ba205ad16ed96af1e681520d32d9396c0a539f8331d3263a9170b79b664a346a0b38289fdd719c623ac384a82f10ae0142f4ee44bb4056886ce51550d1a81802e99f4e5cbe9f0a0e528db113c47cf143b91e70e2864f50233edd87ab565751f34e2b03613ad5b4439424688645a54001aec93ca23342c818e9892e501613a93b95711451eb83130764a3ad17245bd0b35c4294f4a3a8a73dfbd00fb4e658b46f2b227494a161a84b4a6f930ba2544a0572480364a8eb3c01c90c2b492b80625aefeb681eeff18ddee5f249a2e27adb96756c1b56434259603ac76a7d5b2f1e09636fd21a60a35ee0e2b753ab82ec407667aba64e03399b7eb05691e78a420acb583a5cd207a14672d6408382b211834e8e419a8e6913968600c1edada21de9bbae8a57cf0a07a22973d347e9bbbca968d691339fcf36738631766b194894bfd6f0a146b78e164ae1a58e21cca02ea5cc0bc34f19d7b23e1672b075a2b2b2b7f6865790f5a934e26696a1eb16f6204ee90423b6f4e58855c7f059ae041cbb512cba8bb5f326ad7cfc1b1ba38b24bcdf751d85bccb8f990f9a851440aa409c42bf91190da8aaf94c381862b5df87eea8f6d4ab1f46e0ece64f23fb187ef4134bc3787ca57fb1bb8627064f65bcf876c695ffa4b536ec89142827c7158d065814de0507e5c9107f5163ddc90fae1e2937fb7443c33a517ed072d2e825ab682849484a20d4c78d596fcc4e26c41a17d668a2c2f16db444dae13b507c8dc8a1cc9009b72a862a819458ed183488bd57fea25d58796cb7e4a7e467d1bf29fdd1f2b2a8d432a238bfe7e3faf3366bf89a13093716882cb7893b14ded7291259e17bfdb49c91ec5100c10decfe04cf086b18d137522b6e395344e4b6bece2ed2f8dd431e0b19441895d23ddd8ff9fa338bb4faca2b74df7286ae70a0e03b9605dc71ead66c6e1e534d74592764574214878b71874cc5b92821442be0ed819761c141976d25a6c67d2ca5a7b1d24cd706a5fb63442394a9e978bffdf53326ed7940443c844dbe7b06bd2d988152d1d887f46ea3fbb6da8ea7f284d3ddc6013c4b27d7adbc7cf3555f8a2bf2725af30ec1f5ee38866217bed2435a2cd10cd7978c03c056ac55fb19232def8dc2a6d7f4399f9eea50ec96efc43904fb3bafcafe6f0dc7bb1b1b941c543799d608ac7d635a580201e1cf0b9e7a866e2b3adcac0595d2e5b76785834ed4a26c8fb8685c2b180e158a50c45f5469830f46e27511778fe335a37221c70412a88ba43a1ce48893168d484b327619562a5ca7bb0b2fff385fba84a40cdaa408b7e9f56367053d9e691a2d4ea83ee8a2f9961102588a4501bce8fe75b792fb1c35ab6fdb04428f63f950d794bb8f262982965f10d6deb7fdb2af131c943e98b9fab4b78546a6c235e806c5a276141ea5deef8b5189dcc2c28e97ea84a37d51998ba12cd6fd4f8c2127d6a3902120d5e9de2a8e9227fedf72524e635f4735175fcdda99f4708b41cf3a3f9d6fed256fe7eca20d8e434d01b9d47f469b41c3dddf5d3b2b61c6e1644efc3a281fe57b09722a85aa3bec76eb133e25ae23b688fde89750cf7369fcc774116918c90b5322904045d2ae7815c948d6d1a039e60375d4a00c05a4f87198157ec77b88a4d61aebb7dc37b79bc990ac982420dc63bed6f73c7b3dc3aedcd5088878aa1e200c875bcaef75f680830232a61f82763a2d37f94a0d5e07ffc6f0513bc079b60b88d656bb5321b2253ba39952cb7d59f13d8d351550ee55210ea63d2149000ae261496ab23f268be60f5983baf8d1a1b4d70e781f4ad8ab9e4253ed6b4206ab9dd2133876724be474d3d33d36fad2a979c81cd5e3ab379193a3cded6c256e86d80b80a064d8956dc14606feeb66c9a263e6566175d39359235cb121c32cbe3a81ca2d138c7a5d864249bdefcc5a156394559548ba6a412f2cdb5bbe17793915139d117267d1c0ebe728a7e84fec2ab3ca8eff4204d31062702cd775c95aac2d9abd99f27ed247685d35a368df0b712759c00bf4627578b4bd160e26a01c84e2c5852a464be147654aa4ed62b52ec1392a1de898dddfcf7ec8e364b48c22b21256cf31f0397b32e2c7c904b676bd5d4c04322204a8b2f3266c1e4563cfd0e7fe7b85d6f9e90f2f3ee4be031407a0bb5f4b31d0904ee37700ac326e9564214f06962816f5cd535c48ec42cd77f72bc48d9aeca1eba7195412993fca1ec08d09a7c91bc8da46ce0abf776f4f74b77c69334b7ce92a64e33891ffce045cf71e7e73b290a707bfe82c2f79f6e9c5708a12249b0069591514333e967bd34e4ff8e9e2b54138ad203add1b8b5a3b00a0bf893a2db1586e1546c6254d82aa8cdd48aa71a0ab1b14ae04bb5bf692da5b9a3645978802b643a7a9f1d4ac8335099a221f4b54ee87ef577a3800da983b120671bd00c1eb44153a538b7706fe114d774d74321d0700b613e4d4ad816fb0d7b3cd41a94d4b9b34426d26f071f352538a1bad85d4b89268f377f0197b1962b1a5638f8af1e06a3307ff05ff3cbbe250942e03b225f43bdbb1e02ecd4fc75a806b3addede53c65e43ae0d5f975238db332cd1a730967eb721a1e4b85b171d68568dcb9d61d8be424cc7bfb5313e9b1e208c8ff262f1189fefdaa5927ec7d06e3a9bf817f08a48d6dc23922ad4c15c8f19a60ed9c190529774760665e4fc4f7819e7c620ac6641085c4453c429d8541af0be5d93c2240f7d3766d3afd949de71902d0a5bd0a31bed3d40722a8b5daa846734c62b91c051d3975f70ef2e0081f029b1817691efa19e21fab92da1aec19d0e8513955cde3f6f1fc566d55d9797d4eab4531d3f884ab812debeaeeabc4e4639a128fbe71bf2f83b374ce6372ceb386a9254eba9894f911f068cf19be7e1bed43bd52da9b9a257e95c4c6aa8f6ea75a74f41342732461f2a271b5eba7b3aa8c963a5378c6655d75615540084c0cac58bef68caa8ef444383370e5a3723af1c516df75915cc2792927b5c3fc30678f7b228b21eb5aa03be754958d6ee3ca9bdbf1313eb5660a6f7578af8169a56505934d8dfd62425c6e3cdd0f817275f265957933bb040ea484e04d35de512ae1dac0a479b95383265c24666992bf1b4af4e85fdad541d42cfc99224024919a3ae9e0af37e436a9bc02fbd517ab0e10897f633e072ef4541e33cac24697e44eaa5debc619b770140c7fe5ffe9858dcdb3c2dd60a3faa6fd809cd16632d5deed9130cece74f472a97e79cabbda27c85814f5f8b7767fda0d778b4e43c9e4317e695e299a1539bb78e4af45a7a5c94ecae2aec3152d7410c6906338fb3a27e67caf61f640fa9ee0b2a011e49886e764d38661aa123fb0e0dfa8945d4f5d52eeb3a48914e0c36503ea4e003e26e7d9a18bd9c902bed4c4f5a9bee54f353be6b20f988d83d4e9f07371b5b55f149a61458d6cb384193ba204d90536c4437e2466085a8148d1faef3bc46899f8b92040614cd335783f26cdebe8aeb40a3c137f3d74fada5bdf36a92ddcbb701de3425000b7d005e7a6902775c54c3b113fdde854afcad800f4a97b382f4be4daa07e3a037e08e4fb91b5d98c68d102e2374202c1d484168cd60da115039b65a938506dde028456f76a020db8a2d61492991c52458cd40bf96b3ddb01bbe9976c9cb111e82cb9857c25dc74bada01e4c91d28463b19690ec6de471d3beaf18a873a642d681f159e7d6b57878000f70bfff608e1e59213240802146523c9363033e651b19ccd7741ed9399694c5fbabbd3aef6b647b64ba366d49738cab83b23bf0a99b7be0433aaf05048167f8997c0d8141803eccb0bc88f26316bebd14b93f7542488c1d986b7ed32a79b675b121d27122b9ca17689d6d1812d3e2c67ef36cdec2186b4bce8c2195559876eb250c4649561c7c21fc6130508bcd1767b8b6ae407afea550ec7da25b330afb3f32d2bbec463cb05032d00c11a40d3e6b0ab83da098ee08406317c6cb0ceb9b12ecef22625441a68384152e1587139b553ff149610f877f40f5802d93c534c42f279fccb2858df57947f296cb160aca2b25d7a8502e8a24b99b2dd538b19f46fbff21b675e0860b3e83f91e35d5561f8cc6fa4bc2bbae1e2e0c435aa6816b9cce5630142e58d544")); // 3072 bytes
    assertMore(ev1.size(), COAP_BLOCK_SIZE);
    assertTrue(ev1.contentType() == ContentType::BINARY);
    assertTrue(checkStatus(ev1, CloudEvent::NEW));
    // Event 2
    assertTrue(ev2.name() == scopedEventName("abc2"));
    assertTrue(ev2.data() == Buffer::fromHex("1ea9d82dee3b4672127e643f0cdf83dedc1c594c583b9917b769a89cd2eb97fc3841e76d31b6f2f2e8c1e22564b5bebffd9983e759c68cf39494d97db719eb582e7cde51e1d7e9c5496fb19c12ae3cb5f9c249652e52c8fa51dcdcdf9d37576388f8fa2e1065312806eb83a8c037075eb5e09b4c2e46f1ea8d0fe0edf02178abeda4497efd3a38849c2a985ad2dfb39767c9ac03a7631ad8daec1c46d581493856206c0a838d8cb9d9831f18665230375c48670825812c4dcd37f6e74c5cd0174967c72842148c346ef5b523df7d9e4ae7aff15e82f92418c9afa608562d6143515d6abf03f7d2325bf15385275bbef122ad53b17ef52f0c623f09f31b4baf8b0fe161d5eb6b62ebaa7245dcbaf42874b4d038036246cc2e92b5e83cd1c852b0c6168854ee780610a2e4b72831d1e9eab5bf9d254e03bc88d8d7690d7ae11281a112bb77fcaf2c2db82cfe0a045b3db05faac56ddf4200067745e2a7d335f6f57891d6398578ef2acbd57e8f83950126b250c03b8b45c6f0a37ee97031adcc9f9cff481e0e722a82cb5ae2fe440c7fab8f1cc7e52a54576877351ec35a4f2941f6de5e6961e7ac4cf4444b755a446ca3e5c4e5aca3361084e830c4266ebe6412d12413436f250d03adc393bceb04d02ae4e61c589e2ae8b534f6ee3e14889c7125645cb8a80c9fde369f0cee5b2a97503d6a5984148a1326de70ae0e43535db17f79d8eaf326f04724e23685cd2ff578329d6d9359b047e1963adeaa17453757b5fabe3568259ff3d85e518471afc318545fa0839c73fcbc9f549edef07e005caf8a01f3ebec6347f37591decaaa3013bcc1dc5c085f5d25cda56612ee7e769006c58d7dfcdb0e6eb6a131e9a068d5c4dcc1434c87673f7d745fef5038082011a8e0505eda5cddf7f1bfc2d63b6be0c3379cff4dadb78cd952cdc530407845e573e435a31db99704ca8b1664bf9e59eed290ddc9e604bffeae41eca14380c123d04dddf34229438584ff2f8ce0300232c03101b10bf65aef2e04ca15b7c0afac4b9613aa5412eed6fc71d4b7f3e80cc7e873a1d9ca23b7944a0886f39be43ed450233a10e99c322be2d985d365dfe5731ca3028bf820ac91317db5ccaa99652611e16f4c78fc74f192df627531b33ad9b23cebad346fbf9dcde26fb1c50b6c287650c6f66e9018876e3d2bce61f6aa9fa3dc9156f834ddd1491696ac2ae337bfcb9757f0392f34bb3f5be44da8a78b608968006b22c2a661776227bb14737a65e7c14fc3a35defc9034248ab129277bef51416448502b371a7d65e58140cb1ba9ec5d72f71a74820a62cedbef330f9a8ade7d6a852f0286b3c94ff95b671b2c3ff7015901e8279142ea94dc26d2a57abf02a83e4af6686c3112627721f2f12ae1e4650c87c4d4d3fc1cd90ec85b2d87617edaab54b65cce7f3e6a737f0a0d8f19b2ee5d667149e8d8c40bcc5379dceaaacd0721edbc2b03bebe9c9eb81f7ecd4e6aa9ce31fe476ca7bcde58c95f5b9e8e90af1e2898b8dc062f80fbcecd79ed416e46d554231608a43cf148875f20e0e7adcf4f2280a4e9339700944a632e2e8333361972c7810d4b1fa8759ae5f1fe98dc1bbecb0227bf95a21b8abcd010ded5fe04eb84de1bc2432fb51e0902cf2e5add24c06ef417084f84c76056eead98219a51152f8e543b5cda3807976d1f6fc9ce6033e3b9af515d92289044f70940bd10380843ace513a966545320de029820314bad19f72c02abb228cd7336e1ed0340a671d41c206a2e00eb00bc0073e69da811cdc04e72749fe05ed6801d9ef0464747f923e4e314926b459b311a03af2af8b2072f69a57061ab43c9cabb6b09c243ef9078463f004239bbcd3872ee8b7df3afdab324e6b23837a7a383d5aef95578a6aa90e8daaf0cdb847dd3d8161a6a83828931e944afdbd87ee44d686b53a59cde3c62bf72140d85ef6324cd0be899529d468069c4c79987eb1e8a50273289d4832c6747ea1747f96285513b9be609247687d4143ff67774a6962456e5c82eb5206da980f03a3f206c0f8a9ca4316649653ca5a997d4a98a8b7237a4e60dd944c6e4cec522422bb5f606ede100ca77b7630091d12a5e70dd43d3858b483e4371a2ddc79c1b8d034301787614bceac21997e5ab192329b0c099def5a6196503238751028c552eb84338416c44dd32eeb2c82fe37fa9389142e043b467a10db4b7a37a006c69d457943fe0c7f746be57632ac486ef5e60d249b69979dafa0a41695dd86acfb58eea6e0e6d0e1b3cdce5e3891464771120f60816c500000506dc590532bb7ca13a6b6c0ec9cdbd4cd2ba332aec259e6b19ceb8b679e3c0ec6c47d90ad67f195f2ad941d9f7bb8a89d95bb0b69611ab9e5fe0b226711ffd3761bc4fafd8ddf5d4bb3e83087a63a769936667c0d693c66ee2485964d798566e0bb261e1f1de500a49f471ce14d74118f9772ddac26a0315a20e19be2cc49db36ca70edc32ece854ac240c1b604afdb0c51b82d692a7fe5d0f454f1e21343ca4402a5f56292b0b62465c980c2fd924a147174d614adb5fd54385c5a8eceee04142080cd24bb966d1146a4e32c243384b8943ec884d040d2e88d94b1914e1ddc10870e547f404fcf7b8e35eb95e5d184f9b392b60c7a8ddc852be3f97634f988ca21949528f3fe7f15d33652916a04e22611701db30e5bc88e6bcf52cd783552bb4ece8b8a9f3f87d01079881d92385e4ac10fba58722636551a50d7b8ba9ff9b9deb2a70438c69f34729aebf678ff74b640567284e75a3e1152cb24debfa29074efd8c240277112d5c98c4c4e4df020993da3a3264cb5de6478c60feba4e0eb365db016535505a116e0d0b8d7af534f07a59d05ab640e48215624dbb8cd0715a7a3a1bd7bd7b45c069d5b435ee7af1283aca2500752b53191f7d35f103e41fb7fb9037fad1d085e1078d07bca330538e786e889051577b203d50c3154ef0d5bdb8316e50bddd001cb770c08375487c3b86837a8cf101a0a2106b2f79ddb6758b4beb0b41826eaf68e9d1bbfada01bac5fb49120529b4c2f11967d6cae9058b358a66088d8832a7a876e27b3d19210a0f617bde82041dd4951981f53e2d26afa959a78c85aed2567deefc952bd68abfa8e960c08d751752dbd78c615719fd10bd106ed1314f2b98d20ca05616d7926abee3334b114abfc44b6a7c11bf9523b2f0305d09eb35e302f6569a39413e322d7882792759b90ea29011cd6f28687874a434a2ff0ff68bdc8fab626e7457f6d12f94cf5708c9880f9aca97654b474870dc88c9b70b1ec57a5d3fe6f7c5458e5a55322ba69fd34f76019546508fcd1ace22b0e25319a814e9ed0deb9e1dd884ae8d2d4528794fb967e4002c072436692c020932c52dbe7d3814a360e8018177523f4acf0921fc29ffde1b14c231c1113fb612653b9f5734d4b8c020dfe9ccec45c1b548f6a610e7b9ac340f2e506d1c7c48373885fdb33c50616c892a58ad039f5526ba083eb35867da499852098aa79a50da7bdbd9b281d35b390f6bec67a68b6114b7888228479d0a891886058c6a908aab19eda584d56e7c351c1eada3641ab10ea7ab8d88dfdc4446ebcb8f75ce9dd943b2293af964559585167283d3babd6c9216f53c6296f1f25165983a28e0657817bc6ff12a79c09c1448fa3ff689b883e79601e8626bc2cd60a1dc34990b64116eba42419d1a2abd781e82d5e5756f41575a44eaedaafc4ba023c847ecb35ef206e83d54c8fa18691225452512370253339f4f449c380d3df037f21f4c58fc78a2683dd3cf9557de9a5d55e9bd09d4578d61c333299d3ba62324dfdf3784e1f27fc9c233a2b9eb6733f87ce4499e8468f05eadc8b3d53fc9e891f62a6a8210f94a08d1a3780eeb1447f1ba99c1b813dbf0bbc67d5f50919a1547157cf252311a653750ef76972bc5a46cf246ce8eb8cfe24774e96f1051f218aa6206f55a58467c9922297bcda40e89403d932dee4ca0f83de1d3498f95ba96e49d8cf176b5909bb73ea635d6fb0efabd75ee90608f7fd1fd5675ae0fe28437e4f6af9c7d7512e576301b91d82f79ff330033a12d0b2b399ee110fb2132fedaf773e4239318b1b3fed49285cfff5559f4a69c9e1103aea3ec5b6ec7dc840558bf2e79551132cabd5728641c2d5b0368b829b5db7dc45143c0f77f06754c0603b8331fac954b67cb33327ef443c84d08db58c13b11f9af8966d5b558a7e70fce24fc4f3c97366887dec46c59297e70991d164f7cda544c066c5b356a395225072cb93f4ab3ef06d89171970d846938adb5ecd7ad5e738b384a8ad1630b456a3be51c0c6cf6a4da8b8")); // 3072 bytes
    assertMore(ev2.size(), COAP_BLOCK_SIZE);
    assertTrue(ev2.contentType() == ContentType::BINARY);
    assertTrue(checkStatus(ev2, CloudEvent::NEW));
}

test(20_init_new_api_subscription_is_preferred) {
    events1.clear();
    oldEvents.clear();
}

test(21_new_api_subscription_is_preferred) {
    // For the first event, there's only a subscription registered via the old API
    assertTrue(waitFor([]() {
        return !oldEvents.isEmpty();
    }, 30000));
    auto oldEv = oldEvents.takeFirst();
    assertTrue(oldEvents.isEmpty());
    assertTrue(oldEv.first == scopedEventName("abc"));
    assertTrue(oldEv.second == "rl1HxbPn3GL87Qiz9l8t2or5pYWLt2BfjPgqJuWUwYgXKJuGpBfO5BVyPUHKvQeC0XyOT3qzjfcORoLwatDZb1SxwpMVjjw4oGFn"); // 100 bytes
    // For the second event, there are two subscriptions that match the event name but only the handler
    // registered via the new API must be called
    assertTrue(waitFor([]() {
        return !events1.isEmpty();
    }, 30000));
    auto ev = events1.takeFirst();
    assertTrue(events1.isEmpty());
    assertTrue(ev.name() == scopedEventName("abc1"));
    assertTrue(ev.dataString() == "exhbRKvZmw2SUyrri7NYqu77jZkcpoxbNyv3SuROtKwn8Nwv09GxhZMsaSaz5OLWh4ccoat2zQ2arZhdrOKrpIT5ktdAwzpIEFl2"); // 100 bytes
    assertTrue(ev.contentType() == ContentType::TEXT);
    assertTrue(checkStatus(ev, CloudEvent::NEW));
    delay(3000);
    assertTrue(oldEvents.isEmpty());
}
